首页 > 代码库 > perl C/C++ 扩展(五)

perl C/C++ 扩展(五)

perl 的C++扩展,返回值为自定义类型。

在 perl C/C++扩展(三) 中,我已经介绍了,如何让perl 认识 c++的类,但是前面的介绍中,包括我参考的博客http://chunyemen.org/archives/493,都提到,返回值必须是基础类型。对于开发者而言,如果返回值只能是基础类型,那么对于扩展的开发热情就大大降低了。楼主排除万难,终于在《高级perl编程(第二版)》.((美)simon cozens)一书的第十八章与第二十章中得到些许启发。

下面我来介绍一下玩法。

首先创建一个新的工程,名为Cat

h2xs -A -n Cat

创建好工程后,进入Cat目录

cd Cat

创建一个mylib目录,并将c++代码拷贝到mylib目录下

mkdie mylib

Cat.h 

#ifndef INCLUDE_CAT_H#define INCLUDE_CAT_H 1#include <iostream>class Cat{public:   Cat(char *,int);   Cat(const Cat &);   void display();   char * getName();   int getAge();   ~Cat();private:   char * name;   int age;};#endif

Cat.cpp

#include "Cat.h"Cat::Cat(char * name, int age){   this->name = name;   this->age = age;}Cat::Cat(const Cat & incat){   name = incat.name;   age = incat.age;}void Cat::display(){   std::cout<<"~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~"<<std::endl;   std::cout<<"name="<<name<<"\tage="<<age<<std::endl;}char * Cat::getName(){   return name;}int Cat::getAge(){   return age;}Cat::~Cat(){}

Animal.h

#ifndef INCLUDE_ANIMAL_H#define INCLUDE_ANIMAL_H 1#include <iostream>#include "Cat.h"class Animal{public:   Animal(char *, int);   void display();   Cat * getAnimal();   Cat * setAnimal(Cat *);   bool haveAnimal();   ~Animal();private:   Cat * cat;};#endif

Animal.cpp

#include "Animal.h"Animal::Animal( char * name, int age):   cat( new Cat(name, age) ){}void Animal::display(){   std::cout<<"~~~~~~~~~~~Animal.display()~~~~~~~~~~~~~~~~~\n";   cat->display();}Cat * Animal::getAnimal(){   return cat;}Cat * Animal::setAnimal(Cat * lcat){   //delete cat;   cat = new Cat(*lcat);   return cat;}bool Animal::haveAnimal(){   //return false;   return 1;   //throw 1;}Animal::~Animal(){   //delete cat;}

在mylib 目录下创建Makefile.PL 文件

mylib/Makefile.PL

 1 use ExtUtils::MakeMaker; 2  $Verbose = 1; 3  WriteMakefile( 4  NAME   => Animal::mylib, 5  SKIP   => [qw(all static static_lib dynamic dynamic_lib)], 6  clean  => {FILES => libanimal.so}, 7  CC   => g++, 8  ); 9 10 sub MY::top_targets {11 12 all :: static 13 pure_all :: static 14 static ::  libanimal.so 15 libanimal.so: $(C_FILES)16    $(CC) -shared -fpic -g -Wall -o libanimal.so $(C_FILES) 17    $(RANLIB) libanimal.so 18 ;19 }

注意:mylib/Makefile.PL 的16 17 行前的不是空格键,而且table 键,因为这里是Makefile的代码,如果不按照Makefile格式编写,make 操作就会报错

退回Cat 目录

cd ../

创建一个typemap 文件,并写入如下内容。

typemap

TYPEMAPCat * ANIMAL_OBJECTOUTPUTANIMAL_OBJECT   sv_setref_pv($arg, CLASS, (void *) $var);INPUTANIMAL_OBJECT   $var = ($type) SvIV((SV*) SvRV($arg));

这个typemap 文件是让xs 识别新定义的类。在(三)那里是在Makefile.PL 文件中指定了一个perlobject。map的文件,其实内容和这个差不多,我们没有必要定义那么多的别名。

修改Ma ke f ile.PL

 1 #use 5.018002; 2 use ExtUtils::MakeMaker; 3 # See lib/ExtUtils/MakeMaker.pm for details of how to influence 4 # the contents of the Makefile that is written. 5 $CC = ‘g++‘; 6 WriteMakefile( 7     NAME              => Cat, 8     VERSION_FROM      => lib/Cat.pm, # finds $VERSION 9     PREREQ_PM         => {}, # e.g., Module::Name => 1.110     ($] >= 5.005 ?     ## Add these new keywords supported since 5.00511       (ABSTRACT_FROM  => lib/Cat.pm, # retrieve abstract from module12        AUTHOR         => chen <chen@>) : ()),13     LIBS              => [‘-Lmylib -lanimal‘], # e.g., -lm14     DEFINE            => ‘‘, # e.g., -DHAVE_SOMETHING15     INC               => ‘-Imylib‘, # e.g., -I. -I/usr/include/other16    # Un-comment this if you add C files to link with later:17     # OBJECT            => $(O_FILES), # link all the C files too18     ‘XSOPT‘           => ‘-C++‘,19     ‘CC‘              => $CC,20     ‘LD‘              => ‘$(CC)‘,21 );22 23 sub MY::postamble {24 ‘25 mylib/libanimal.so: mylib/Makefile26    cd mylib && $(MAKE) $(PASSTHRU)27 ‘;28 }

红色的代码为添加或修改代码。注意:26 行代码前是table 键,而不是普通空格

函数 MY::postmable 是往Makefile 文件插入的一段代码,作用是让编译Cat的扩展库之前,首先编译依赖的libanimal.so包。

其余添加的代码主要是定义编译使用g++。再一次提醒,需要注释第一行代码#use 5.018002;

 修改Cat.xs 文件

Cat.xs

#ifdef __cplusplusextern "C"{#endif#define PERL_NO_GET_CONTEXT#include "EXTERN.h"#include "perl.h"#include "XSUB.h"#ifdef __cplusplus}#endif#include "ppport.h"#include "mylib/Cat.h"MODULE = Cat      PACKAGE = CatCat *Cat::new(char * name, int age)voidCat::display()char *Cat::getName()intCat::getAge()voidCat::DESTROY()

格式和之前的(三)一样,这里就不再展开讲了。

生成Makefile 文件,编译

perl Makefile.PL && make

割一下############################################################ 

第二部分,创建Animal的工程

h2xs -A -n Animal

将Cat工程的mylib 软链接过来

ln -sf /home/chen/learn/perl_c/Cat/mylib /home/chen/learn/perl_c/Animal/mylib

创建并编辑typemap 

 1 TYPEMAP 2 Animal * ANIMAL_OBJECT 3 Cat * CAT_OBJECT 4  5 OUTPUT 6 ANIMAL_OBJECT 7    sv_setref_pv($arg, CLASS, (void *) $var); 8 CAT_OBJECT 9    sv_setref_pv($arg, "Cat", (void *) $var);10 11 INPUT12 ANIMAL_OBJECT13    $var = ($type) SvIV((SV*) SvRV($arg));14 CAT_OBJECT15    $var = ($type) SvIV((SV*) SvRV($arg));

实际上,这里就是本次博客的核心。

我们仔细观察Animal工程的 typemap 和Cat 工程的typemap 有什么不一样。Animal 工程的typemap 多了一个CAT_OBJECT 的定义,并且在 CAT_OBJECT 的 OUTPUT 中(第九行),是写明指向Cat的类。

如果我们仔细看一下前面的Cat 工程编译命令,有

g++ -c  -Imylib -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fstack-protector -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -O2 -g   -DVERSION=\"0.01\" -DXS_VERSION=\"0.01\" -fPIC "-I/usr/lib/perl/5.18/CORE"   Cat.c

Cat.c这个文件是在make 命令执行时产生的文件,我们打开Cat.c 文件查看的话,会发现一些有趣的东西

XS_EUPXS(XS_Cat_new){    dVAR; dXSARGS;    if (items != 3)       croak_xs_usage(cv,  "CLASS, name, age");    {   char *   CLASS = (char *)SvPV_nolen(ST(0));   Cat * RETVAL;   char *   name = (char *)SvPV_nolen(ST(1));   int   age = (int)SvIV(ST(2));   RETVAL = new Cat(name, age);   ST(0) = sv_newmortal();   sv_setref_pv(ST(0), CLASS, (void *) RETVAL);    }    XSRETURN(1);}

上面的代码实际上就是Cat::new() 的真实代码。我们可以发现CLASS 的变量,其实就是一个字符串数组。我在实验过程中,将CLASS 字符串打印了一下,发现原来它记录的就是Cat的工程名“Cat"

分析到这里,我们就不难反推,如果需要返回值是自定义的类,我们只需要将”CLASS“ 字段写成我们自己的工程名即可。

有兴趣的同学也可以深挖一下,为什么CLASS 会自动识别当前工程名,与(三)的perlobject.map文件中其他玩法。

 

后面的事情就很简单了,不过是修改Makefile.PL 与 Aniaml.xs 文件

Makefile.PL

 1 #use 5.018002; 2 use ExtUtils::MakeMaker; 3 # See lib/ExtUtils/MakeMaker.pm for details of how to influence 4 # the contents of the Makefile that is written. 5 $CC = ‘g++‘; 6 WriteMakefile( 7     NAME              => Animal, 8     VERSION_FROM      => lib/Animal.pm, # finds $VERSION 9     PREREQ_PM         => {}, # e.g., Module::Name => 1.110     ($] >= 5.005 ?     ## Add these new keywords supported since 5.00511       (ABSTRACT_FROM  => lib/Animal.pm, # retrieve abstract from module12        AUTHOR         => chen <chen@>) : ()),13     LIBS              => [‘-Lmylib -lanimal‘], # e.g., -lm14     DEFINE            => ‘‘, # e.g., -DHAVE_SOMETHING15     INC               => ‘-Imylib‘, # e.g., -I. -I/usr/include/other16     ‘CC‘              => $CC,17     ‘LD‘              => ‘$(CC)‘,18    # Un-comment this if you add C files to link with later:19     # OBJECT            => $(O_FILES), # link all the C files too20     ‘XSOPT‘           => ‘-C++‘,21     #‘LDDLFLAGS‘       => ‘-r‘,22 );23 24 sub MY::postamble {25 ‘26 $(MYEXTLIB): mylib/Makefile27    cd mylib && $(MAKE) $(PASSTHRU)28 ‘;29 }

红色部分为增改内容。

注意:27行代码前的为table 键,而不是普通空格

Animal.xs

 1 #ifdef __cplusplus 2 extern "C"{ 3 #endif 4  5 #define PERL_NO_GET_CONTEXT 6 #include "EXTERN.h" 7 #include "perl.h" 8 #include "XSUB.h" 9 #ifdef __cplusplus10 }11 #endif12 #include "ppport.h"13 #include "mylib/Cat.h"14 #include "mylib/Animal.h"15 16 MODULE = Animal      PACKAGE = Animal17 18 Animal *19 Animal::new(char * name, int age)20 21 void22 Animal::display()23 24 Cat *25 Animal::getAnimal()26 27 Cat *28 Animal::setAnimal(Cat * lcat)29 30 bool31 Animal::haveAnimal()32 33 void34 Animal::DESTROY()

生成Makefile并编译

perl Makefile.PL && make

编写测试代码,test.pl

 1 #!/usr/bin/perl 2 use Animal; 3 use Cat; 4 $animal = new Animal("chen",123); 5 $animal->display(); 6 $cat = $animal->getAnimal(); 7 $cat->display(); 8  9 $name = $cat->getName();10 11 print $name;12 13 $name = "sdjlfa";14 $animal->display();15 16 print "~~~~~~###############~~~~~~~~~~~~~~~\n";17 18 $lcat = new Cat("ASKJKLF",889);19 $lcat->display();20 print "~~~~~~###############~~~~~~~~~~~~~~~\n";21 22 $tcat = $animal->setAnimal( $lcat );23 $animal->display();24 25 $test = $animal->haveAnimal();26 print "@@@@@@@@@$test@@@@@@\n";27 28 29 $animal2 = $animal;30 31 $animal2->display();

添加环境变量

export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/home/chen/learn/perl_c/Animal/blib/arch/auto/Animal:/home/chen/learn/perl_c/Animal/mylib:/home/chen/learn/perl_c/Cat/blib/arch/auto/Catexport PERLLIB=${PERLLIB}:/home/chen/learn/perl_c/Animal/lib:/home/chen/learn/perl_c/Cat/lib

Animal 的扩展包动态库在Animal  工程的 blib/arch/auto/Animal 目录下,pm 文件则在 Animal 工程的 lib 目录下

同样,Cat 的扩展包动态库在Cat 工程的blib/arch/auto/Animal 目录下,pm 文件则在 Cat 工程的 lib 目录下

同时需要将libanimal.so 文件添加到LD_LIBRARY_PATH 环境变量中。

 

运行一下测试程序

perl test.pl

输出:

~~~~~~~~~~~Animal.display()~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~name=chen    age=123~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~name=chen    age=123~~~~~~~~~~~Animal.display()~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~name=chen    age=123chen~~~~~~###############~~~~~~~~~~~~~~~~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~name=ASKJKLF    age=889~~~~~~###############~~~~~~~~~~~~~~~~~~~~~~~~~~Animal.display()~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~name=ASKJKLF    age=889@@@@@@@@@@@@@@~~~~~~~~~~~Animal.display()~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~name=ASKJKLF    age=889

测试成功。