saundy 发表于 2015-12-26 07:44:56

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 &quot;EXTERN.h&quot;   
#include &quot;perl.h&quot;   
#include &quot;XSUB.h&quot;
  #include &quot;ppport.h&quot;
  MODULE = Mytest         PACKAGE = Mytest
  /**Manually added functions in XS macro language**/
  void   
hello()   
    CODE:   
      printf(&quot;Hello, World!/n&quot;);
  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/typemapMytest.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=/&quot;0.01/&quot; -DXS_VERSION=/&quot;0.01/&quot; -fPIC &quot;-I/usr/lib/perl/5.10/CORE&quot;   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 &quot;-MExtUtils::Command::MM&quot; &quot;-e&quot; &quot;test_harness(0, 'blib/lib', 'blib/arch')&quot; 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 &quot;test.h&quot;   
#include
  int   
foo(int a, int b)   
{   
    printf(&quot;in foo(int, int)/n&quot;);   
    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 => ,   
    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 &quot;fallback&quot; 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 &quot;Can't copy $fallback to $file: $!&quot;;   
}   
}
  sub MY::postamble {   
'   
$(MYEXTLIB) : testlib/Makefile   
      cd testlib && $(MAKE) $(PASSTHRU)   
'   
}
  其中, 增加MYEXTLIB使得h2xs生成的Makefile.PL 可以找到这里的c lib: testlib
  修改生成的Mytest3.xs 文件:
  #include &quot;EXTERN.h&quot;   
#include &quot;perl.h&quot;   
#include &quot;XSUB.h&quot;
  #include &quot;ppport.h&quot;
  #include &quot;testlib/test.h&quot; //using the relative directory
  #include &quot;const-c.inc&quot;
  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。
页: [1]
查看完整版本: PERL XS Tutorial