设为首页 收藏本站
查看: 841|回复: 0

[经验分享] perl C/C++ 扩展(五)

[复制链接]

尚未签到

发表于 2015-12-26 10:59:00 | 显示全部楼层 |阅读模式
  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



TYPEMAP
Cat * ANIMAL_OBJECT
OUTPUT
ANIMAL_OBJECT
sv_setref_pv($arg, CLASS, (void *) $var);
INPUT
ANIMAL_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.1
10     ($] >= 5.005 ?     ## Add these new keywords supported since 5.005
11       (ABSTRACT_FROM  => 'lib/Cat.pm', # retrieve abstract from module
12        AUTHOR         => 'chen <chen@>') : ()),
13     LIBS              => ['-Lmylib -lanimal'], # e.g., '-lm'
14     DEFINE            => '', # e.g., '-DHAVE_SOMETHING'
15     INC               => '-Imylib', # e.g., '-I. -I/usr/include/other'
16    # Un-comment this if you add C files to link with later:
17     # OBJECT            => '$(O_FILES)', # link all the C files too
18     'XSOPT'           => '-C++',
19     'CC'              => $CC,
20     'LD'              => '$(CC)',
21 );
22
23 sub MY::postamble {
24 '
25 mylib/libanimal.so: mylib/Makefile
26    cd mylib && $(MAKE) $(PASSTHRU)
27 ';
28 }
  红色的代码为添加或修改代码。注意:26 行代码前是table 键,而不是普通空格
  函数 MY::postmable 是往Makefile 文件插入的一段代码,作用是让编译Cat的扩展库之前,首先编译依赖的libanimal.so包。
  其余添加的代码主要是定义编译使用g++。再一次提醒,需要注释第一行代码#use 5.018002;
  修改Cat.xs 文件
  Cat.xs



#ifdef __cplusplus
extern "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 = Cat
Cat *
Cat::new(char * name, int age)
void
Cat::display()
char *
Cat::getName()
int
Cat::getAge()
void
Cat::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 INPUT
12 ANIMAL_OBJECT
13    $var = ($type) SvIV((SV*) SvRV($arg));
14 CAT_OBJECT
15    $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.1
10     ($] >= 5.005 ?     ## Add these new keywords supported since 5.005
11       (ABSTRACT_FROM  => 'lib/Animal.pm', # retrieve abstract from module
12        AUTHOR         => 'chen <chen@>') : ()),
13     LIBS              => ['-Lmylib -lanimal'], # e.g., '-lm'
14     DEFINE            => '', # e.g., '-DHAVE_SOMETHING'
15     INC               => '-Imylib', # e.g., '-I. -I/usr/include/other'
16     '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 too
20     'XSOPT'           => '-C++',
21     #'LDDLFLAGS'       => '-r',
22 );
23
24 sub MY::postamble {
25 '
26 $(MYEXTLIB): mylib/Makefile
27    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 __cplusplus
10 }
11 #endif
12 #include "ppport.h"
13 #include "mylib/Cat.h"
14 #include "mylib/Animal.h"
15
16 MODULE = Animal      PACKAGE = Animal
17
18 Animal *
19 Animal::new(char * name, int age)
20
21 void
22 Animal::display()
23
24 Cat *
25 Animal::getAnimal()
26
27 Cat *
28 Animal::setAnimal(Cat * lcat)
29
30 bool
31 Animal::haveAnimal()
32
33 void
34 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/Cat
export 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=123
chen~~~~~~###############~~~~~~~~~~~~~~~
~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~
name=ASKJKLF    age=889
~~~~~~###############~~~~~~~~~~~~~~~
~~~~~~~~~~~Animal.display()~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~
name=ASKJKLF    age=889
@@@@@@@@@@@@@@
~~~~~~~~~~~Animal.display()~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~
name=ASKJKLF    age=889
  测试成功。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-156488-1-1.html 上篇帖子: Perl 常用函数 下篇帖子: perl 安装 Net::Telnet 模块
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表