zoukankan      html  css  js  c++  java
  • PERL XS Tutorial

    XS: eXternal Subroutine, 也叫做xsub. 它是perl代码中调用c或者c++ 子例程的接口。

    在许多情形下,如一些CPU或内存密集型的任务,单纯使用PERL很难满足这些需求,这时,可以使用c或c++语言完成该任务,提供好接口后,通过XS接口可以直接被PERL 代码以module的形式调用。

    环境:

    OS :debian 5 lenny   PERL: This is perl, v5.10.0 built for i486-linux-gnu-thread-multi

    创建一个perl 扩展, 通过命令h2xs

    h2xs –A –n Mytest

    其中:-A 选项是 --omit-autoload , -n 选项用来指定新的module的名称,这里是Mytest。

    生成文件的列表:

    Changes: 记录新建扩展的修订日志。如果你使用版本管理工具,如cvs,svn,可以不关心这个文件。

    MANIFEST:

    Makefile.PL: 通过该文件可以生成Makefile。命令perl Makefile.PL

    use 5.010000;
    use ExtUtils::MakeMaker;
    # See lib/ExtUtils/MakeMaker.pm for details of how to influence
    # the contents of the Makefile that is written.
    WriteMakefile(
        NAME              => 'Mytest',
        VERSION_FROM      => 'lib/Mytest.pm', # finds $VERSION
        PREREQ_PM         => {}, # e.g., Module::Name => 1.1
        ($] >= 5.005 ?     ## Add these new keywords supported since 5.005
          (ABSTRACT_FROM  => 'lib/Mytest.pm', # retrieve abstract from module
           AUTHOR         => 'root ') : ()),
        LIBS              => ['-lm'], # e.g., '-lm'
        DEFINE            => '', # e.g., '-DHAVE_SOMETHING'
        INC               => '-I.', # e.g., '-I. -I/usr/include/other'
        # Un-comment this if you add C files to link with later:
        # OBJECT            => '$(O_FILES)', # link all the C files too
    );</ROOT@>

    Mytest.xs

    #include "EXTERN.h"
    #include "perl.h"
    #include "XSUB.h"

    #include "ppport.h"

    MODULE = Mytest         PACKAGE = Mytest

    /**Manually added functions in XS macro language**/

    void
    hello()
        CODE:
            printf("Hello, World!/n");

    int
    is_even(input)
            int input
        CODE:
            RETVAL = (input % 2 == 0);
        OUTPUT:
            RETVAL

    void
    round(arg)
            double arg
        CODE:
            if (arg > 0.0) {
                arg = floor(arg + 0.5);
            } else if (arg < 0.0) {
                arg = ceil(arg - 0.5);
            } else {
                arg = 0.0 ;
            }
        OUTPUT:
            arg

    ppport.h

    README

    t/ : 单元测试目录

    lib/: perl 扩展module的目录, PM文件。

    生成Makefile文件:

    perl Makefile.PL

    输出:

    Checking if your kit is complete...
    Looks good
    Writing Makefile for Mytest

    编译

    make

    输出:

    cp lib/Mytest.pm blib/lib/Mytest.pm
    /usr/bin/perl /usr/share/perl/5.10/ExtUtils/xsubpp  -typemap /usr/share/perl/5.10/ExtUtils/typemap  Mytest.xs > Mytest.xsc && mv Mytest.xsc Mytest.c
    Please specify prototyping behavior for Mytest.xs (see perlxs manual)
    cc -c  -I. -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -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.10/CORE"   Mytest.c
    Running Mkbootstrap for Mytest ()
    chmod 644 Mytest.bs
    rm -f blib/arch/auto/Mytest/Mytest.so
    cc  -shared -O2 -g -L/usr/local/lib Mytest.o  -o blib/arch/auto/Mytest/Mytest.so        /
               -lm          /

    chmod 755 blib/arch/auto/Mytest/Mytest.so
    cp Mytest.bs blib/arch/auto/Mytest/Mytest.bs
    chmod 644 blib/arch/auto/Mytest/Mytest.bs
    Manifying blib/man3/Mytest.3pm

    编写Mytest Module 的单元测试文件 t/Mytest.t

    # Before `make install' is performed this script should be runnable with
    # `make test'. After `make install' it should work as `perl Mytest.t'

    #########################

    # change 'tests => 1' to 'tests => last_test_to_print';

    use Test::More tests => 9;
    BEGIN { use_ok('Mytest') };

    #########################

    # Insert your test code below, the Test::More module is use()ed here so read
    # its man page ( perldoc Test::More ) for help writing this test script.
    is(&Mytest::is_even(0), 1);
    is(&Mytest::is_even(1), 0);
    is(&Mytest::is_even(2), 1);

    $i = -1.5 ; &Mytest::round($i); is($i, -2.0);
    $i = -1.1 ; &Mytest::round($i); is($i, -1.0);
    $i = 0.0 ; &Mytest::round($i); is($i, 0.0);
    $i = 0.5 ; &Mytest::round($i); is($i, 1.0);
    $i = 1.2 ; &Mytest::round($i); is($i, 1.0);

    执行测试:

    make test

    输出:

    PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
    t/Mytest....ok
    All tests successful.
    Files=1, Tests=9,  0 wallclock secs ( 0.02 cusr +  0.01 csys =  0.03 CPU)

    最后:

    make install 来安装perl 扩展

    根据c lib 的header file创建perl module

    新建的module名称为Mytest3 建立目录结构

    mkdir Mytest3

    cd Mytest3

    mkdir testlib

    cd testlib

    Mytest3完成后的目录结构:

    Mytest3
       ---> testlib

    在testlib 下 创建你的testlib库,这里仅创建一个简单test.c 和它的header test.h, 如下:

    test.c

    #include "test.h"
    #include

    int
    foo(int a, int b)
    {
        printf("in foo(int, int)/n");
        return a + b;
    }

    test.h

    #ifndef TEST_H
    #define TEST_H

    extern int foo(int , int );
    #endif

    在testlib目录下创建一个Makefile.PL, 如下:

    use ExtUtils::MakeMaker;

    $Verbose = 1;
    WriteMakefile(
        NAME => 'Mytest3::testlib',
        SKIP => [qw(all static static_lib dynamic dynamic_lib)],
        clean => {FILES => 'libtestlib$(LIB_EXT)'},
    );

    sub MY::top_targets {
    '
    all :: static

    pure_all :: static

    static :: libtestlib$(LIB_EXT)

    libtestlib$(LIB_EXT) : $(O_FILES)
            $(AR) cr libtestlib$(LIB_EXT) $(O_FILES)
            $(RANLIB) libtestlib$(LIB_EXT)
    ';
    }

    perl Makefile.PL; make 生成libtestlib.a archive文件。

    使用h2xs 生成perl module 框架:

    cd到Mytest3的父目录,执行h2xs –O –n Mytest3 Mytest2/testlib/test.h,其中-O选项表示覆盖已经存在的extension 目录

    完成后cd到Mytest3目录, 编辑刚刚自动生成的Makefile.PL :

    use 5.010000;
    use ExtUtils::MakeMaker;
    # See lib/ExtUtils/MakeMaker.pm for details of how to influence
    # the contents of the Makefile that is written.
    WriteMakefile(
        NAME              => 'Mytest3',
        VERSION_FROM      => 'lib/Mytest3.pm', # finds $VERSION
        PREREQ_PM         => {}, # e.g., Module::Name => 1.1
        ($] >= 5.005 ?     ## Add these new keywords supported since 5.005
          (ABSTRACT_FROM  => 'lib/Mytest3.pm', # retrieve abstract from module
           AUTHOR         => 'root ') : ()),
        LIBS              => [''], # e.g., '-lm'
        DEFINE            => '', # e.g., '-DHAVE_SOMETHING'
        INC               => '-I.', # e.g., '-I. -I/usr/include/other'
            # Un-comment this if you add C files to link with later:
        # OBJECT            => '$(O_FILES)', # link all the C files too
    MYEXTLIB          => 'testlib/libtestlib$(LIB_EXT)',
    );
    if  (eval {require ExtUtils::Constant; 1}) {
      # If you edit these definitions to change the constants used by this module,
      # you will need to use the generated const-c.inc and const-xs.inc
      # files to replace their "fallback" counterparts before distributing your
      # changes.
      my @names = (qw());
      ExtUtils::Constant::WriteConstants(
                                         NAME         => 'Mytest3',
                                         NAMES        => /@names,
                                         DEFAULT_TYPE => 'IV',
                                         C_FILE       => 'const-c.inc',
                                         XS_FILE      => 'const-xs.inc',
                                      ); </ROOT@>

    }
    else {
      use File::Copy;
      use File::Spec;
      foreach my $file ('const-c.inc', 'const-xs.inc') {
        my $fallback = File::Spec->catfile('fallback', $file);
        copy ($fallback, $file) or die "Can't copy $fallback to $file: $!";
      }
    }

    sub MY::postamble {
    '
    $(MYEXTLIB) : testlib/Makefile
            cd testlib && $(MAKE) $(PASSTHRU)
    '
    }

    其中, 增加MYEXTLIB使得h2xs生成的Makefile.PL 可以找到这里的c lib: testlib

    修改生成的Mytest3.xs 文件:

    #include "EXTERN.h"
    #include "perl.h"
    #include "XSUB.h"

    #include "ppport.h"

    #include "testlib/test.h" //using the relative directory

    #include "const-c.inc"

    MODULE = Mytest3                PACKAGE = Mytest3

    INCLUDE: const-xs.inc

    int
    foo(a, b)
        int a
        int b
    OUTPUT:
        RETVAL

    定义PERL的xs接口,使得perl code可以调用testlib的foo函数。

    .xs 代码文件的结构

    如Mytest3.xs所示:

    MODULE = Mytest3                PACKAGE = Mytest3 注意这一行。

    在该行之前的代码片段将被xsubpp保存直接输出到最终的c source code当中。

    在该行之后会被xsubpp根据perl API convention翻译成c souce code。

  • 相关阅读:
    Mysql Group by week
    查询数据库占用磁盘大小
    菜根谭#117
    菜根谭#116
    保护眼睛颜色的RGB数值
    手动释放linux内存和缓存
    菜根谭#115
    菜根谭#114
    菜根谭#113
    spring mvc静态资源文件的引用
  • 原文地址:https://www.cnblogs.com/yuboyue/p/2109855.html
Copyright © 2011-2022 走看看