• 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<<"	age="<<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()~~~~~~~~~~~~~~~~~
    ";
       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 "~~~~~~###############~~~~~~~~~~~~~~~
    ";
    17 
    18 $lcat = new Cat("ASKJKLF",889);
    19 $lcat->display();
    20 print "~~~~~~###############~~~~~~~~~~~~~~~
    ";
    21 
    22 $tcat = $animal->setAnimal( $lcat );
    23 $animal->display();
    24 
    25 $test = $animal->haveAnimal();
    26 print "@@@@@@@@@$test@@@@@@
    ";
    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

    测试成功。

  • 相关阅读:
    导入列Allowed memory size of 33554432 bytes exhausted (tried to allocate 16 bytes)
    图片设置解决CSS下img图片多余空白或者是表格中有空隙Bug的方案
    报表安装Crystal Reports for Eclipse(1)
    百度收购被收购传闻四起,UC 向左Or向右?
    方法格式在<s:iterator>中,将时间输出显示格式化
    参数脚本linux shell 1 变量$#,$@,$0,$1,$2的含义解释
    匹配行UVA 题目10010 Where's Waldorf?
    报表域Crystal Reports for Eclipse(2)
    分区文件系统FAT文件系统
    希望查询windows下安装cygwin后ssh服务无法启动的解决办法
  • 原文地址:https://www.cnblogs.com/chenfool/p/3910452.html
Copyright © 2020-2023  润新知