Perl+XS(四)

今年perl的峰会应该要数Beijing Perl Workshop了,详情去perlchina看看吧。
这次我要演讲的主题是Perl&XS,是描述如何用c/c++这种语言方便的编写perl扩展,有兴趣的朋友不妨互相切磋切磋哟,这里有几篇实例供大家演练一番。

四、二个样例

1,创建模板

[cnangel@localhost works]$module-starter --module=perlxs::test -mi \

--author =Cnangel --email=junliang.li#alibaba-inc.com --license=GPLv3

Created starter directories and files

[cnangel@localhost works]$cd perlxs-test/

[cnangel@localhost perlxs-test]$ ll

total 24

drwxrwxr-x 3 cnangel cnangel 4096 09-05 13:14 lib

drwxrwxr-x 2 cnangel cnangel 4096 09-05 13:14 t

-rw-rw-r-- 1 cnangel cnangel  196 09-05 13:14 Makefile.PL

-rw-rw-r-- 1 cnangel cnangel  111 09-05 13:14 Changes

-rw-rw-r-- 1 cnangel cnangel 1344 09-05 13:14 README

-rw-rw-r-- 1 cnangel cnangel   92 09-05 13:14 MANIFEST

2,编辑Makefile.PL

生成的Makefile.PL内容如下:

use inc::Module::Install;

 

name     'perlxs-test';

all_from 'lib/perlxs/test.pm';

author   'Cnangel <junliang.li#alibaba-inc.com>';

license  'GPLv3';

 

build_requires 'Test::More';

 

auto_install;

 

WriteAll;

模块inc::Module::Install是一个离线的模块,随着第一次运行perl Makefile.PL,便会产生,该工程有了模块inc::Module::Install不会因为平台不同、运行环境不同而在编译过程中发生模块依赖。

读者可以根据Module::Install模块的用法,添加相应的方法,Module::Install模块有如下方法:

1)包相关信息

name

all_from

abstract

abstract_from

author

author_from

version

version_from

license

license_from

perl_version

perl_version_from

2)包依赖相关信息

recommends:运行需要依赖的模块,没有也可以,最好有

requires:运行必须依赖的模块

test_requires:测试需要依赖的模块

configure_requires:配置需要依赖的模块

requires_external_bin:需要依赖的命令

若要使用XS,需要使用C/C++的源代码库,则需要使用:

cc_filesC/C++源码的文件

cc_inc_pathsinclude头文件路径

若是提供的lib库,则需要:

cc_lib_pathslib库路径

cc_lib_links:链接lib库的名字

甚至可以指定编译的选项:

cc_optimize_flags

可以检查一些环境:

can_use

can_run

can_cc

3)依赖包信息检查

如果使用了其他非Perl的库,这些库是如何检查的呢?有2perl模块提供了检查库是否存在的方法:

Devel::CheckLib

Module::Install::CheckLib

模块Devel::CheckLib会用方法check_lib_or_exit来调用库,如下所示:

check_lib_or_exit( lib => [ 'log4cpp', 'libconfig' ], header => 'libconfig.h' );

模块Module::Install::CheckLib

checklibs lib => [ 'log4cpp', 'libconfig' ], header => 'libconfig.h';

相对来说,Module::Install::CheckLib利用了Module::Install的框架,可以使用离线兼容的模式来检查,是个不错的选择。

4)包的安装

对于包的安装,无外乎数据、可执行文件和库,Module::Install也提供了几种方法供包的安装:

install_script

installdirs, install_as_*

no_index

WriteAll

最终Makefile.PL文件如下所示:

use strict;

use lib '.';

use inc::Module::Install;

 

name                    'perlxs-test';

all_from                'lib/perlxs/test.pm';

author                  'Cnangel <junliang.li#alibaba-inc.com>';

license                 'GPLv3';

requires                'File::Find' => 1.00;

requires                'Conf::Libconfig' => 0.010;

test_requires   'Test::Exception' => 0.27;

test_requires   'Test::Deep' => 0.103;

checklibs               lib => [qw(check log4c)], header => [qw(check.h log4c.h)];

cc_inc_paths    '/usr/include', '/usr/local/include';

cc_lib_paths    '/usr/lib' , '/usr/lib64', '/usr/local/lib', '/usr/local/lib64';

cc_lib_links    'check', 'log4c';

can_cc                  or die 'This module requires C compiler.';

no_index                directory => qw(t inc);

 

auto_install();

 

WriteAll();

如果在lib/perlxs/test.pm下没有定义licenseperl version,则会提示一些警告信息;

若库checklog4c以及其头文件没有的话,则执行命令perl Makefile.PL失败后直接退出。

修正的办法有很多,不是用all_from 方法,或者在test.pm模块里面添加对应信息。而库checklog4c的缺失可以下载相关源文件或者安装二进制包即可。

3,建立test.xs

根据前面所述,可以使用一些工具建立模板,比如h2xs,得到内容如下:

#include "EXTERN.h"

#include "perl.h"

#include "XSUB.h"

 

#include "ppport.h"

 

#include "const-c.inc"

 

MODULE = perlxs::test           PACKAGE = perlxs::test

 

INCLUDE: const-xs.inc

 

接着const-c.inc文件里面写C相关函数,const-xs.incXS语言接口。为了不产生多余的两个文件,直接分别替换#include "const-c.inc"INCLUDE: const-xs.inc两行:

int lc(char *str, size_t strLen)

{

        if (strLen == 0) return 1;

        int i = 0;

        for (; i < strLen; i++) {

                if (str[i] >= 'A' && str[i] <= 'Z') {

                        str[i] = str[i] + ' ';

                }

        }

        return 0;

}

 

int uc(char *str, size_t strLen)

{

        if (strLen == 0) return 1;

        int i = 0;

        for (; i < strLen; i++) {

                if (str[i] >= 'a' && str[i] <= 'z') {

                        str[i] = str[i] - ' ';

                }

        }

        return 0;

}

然后新建一个perl函数lcuc;值得注意的是,如果直接写成lcuc会和上面的函数冲突,解决冲突的办法是使用前面提到的前缀方式:

PREFIX = perlxs_

开始写XS函数替换INCLUDE: const-xs.inc了,内容如下:

SV *

perlxs_lc(str)

        char *str

        PREINIT:

           SV *sv = newSV(0);

        CODE:

        {

                if (str) {

                        lc(str, strlen(str));

                        sv = newSVpvn(str, strlen(str));

                }

                RETVAL = sv;

        }

        OUTPUT:

                RETVAL

 

SV *

perlxs_uc(str)

        char *str

        PREINIT:

           SV *sv = newSV(0);

        CODE:

        {

                if (str) {

                        uc(str, strlen(str));

                        sv = newSVpvn(str, strlen(str));

                }

                RETVAL = sv;

        }

        OUTPUT:

                RETVAL

4,修改test.pm

修改perl模块:lib/perlxs/test.pm

除了添加一些pm的特征和pod文档外,还需要加载我们刚才编写的库(最终代码会编译成动态链接库的形式),形式如下:

require XSLoader;

XSLoader::load('perlxs::test', $VERSION);

至此,样例基本完成,可以使用:

perl Makefile.PL

make

make test

末了,根据实际情况再做调整,所示如下:

package perlxs::test;

 

use 5.006001;

use warnings;

use strict;

use Carp;

require Exporter;

our @ISA = qw(Exporter);

our %EXPORT_TAGS = ( 'all' => [ qw(

) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw(

        mylc

        myuc

);

our $VERSION = '0.01';

 

require XSLoader;

XSLoader::load('perlxs::test', $VERSION);

 

sub mylc {

        my $str = shift;

        return mylc($str);

}

 

sub myuc {

        my $str = shift;

        return myuc($str);

}

1;

__END__

...

至此,c编写perl扩展的样例基本完成了。

5C++库的设计

比如有一个c++Goods,其中有addupdatedrop三种方法。

1XS头部

#ifdef __cplusplus

extern "C" {

#endif

#include "EXTERN.h"

#include "perl.h"

#include "XSUB.h"

#ifdef __cplusplus

}

#endif

XS文件内包含c++Goods的头文件goods.h

#include <goods.h>

Using namespace goods;

2)申明包名

MODULE = China::Goods PACKAGE = China::Goods

3)构造对象

Goods *

Goods::new(const char *cfgfile)

此处是直接调用了Goods的构造函数,类似于:

Goods goods;

4)定义类型

由于perl并不识别Goods *是何物,所以需要在工程下建立一个typemap文件,里面存放所有perl不能识别的cc++的各种数据结构和本身perl类型的一一对应关系;此处类型映射关系如下所示:

TYPEMAP

Goods *               O_OBJECT

由于没有输入类型和输出类型的映射,这里忽略过。

5)方法重载

int

Goods::add(int goodsid)

int

Goods::update(int goodsid)

int

Goods::drop(int goodsid)

6)析构

C++一样,perl也提供自动析构的函数,如下所示:

void

Goods::DESTROY()

值得注意的是,只需要在模块(pm文件)里面加入:

require XSLoader;

XSLoader::load('Conf::Libconfig', $VERSION);

而无需额外的函数(除非另外想实现其它的功能)。

C++编写 perl扩展的基本样例也算完成了。

五、总结

若是一般系统管理员,不需要特别掌握XS接口编程语言,一般纯perl的代码基本上就能够满足系统设计的要求,但如果对于一个开发者或者更高级层次的设计者来说,perl扩展所带来的效率,以及它的设计模式带来的灵活性是不可估量的。开发者们可以广泛地利用各种c/c++库做自己的业务,扩展自己的系统。

C/C++perl扩展并不是很困难,熟悉perlapi后,和c/c++一样,重点在于设计和内存的管理上。如果你是一位从未接触perl而写过不少c/c++项目的工程师,可以尝试使用perl,体会它的便捷;如果你是一位系统管理员,也可以尝试写写perl扩展,在不断锻炼你c/c++的写作能力的同时,你会感受到perl扩展的高效以及perl的强大,同时还能接触到perl语言的核心。

希望大家越用perl越开心!

Categories

| | 评论(0)

发表评论

August 2012

      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

关于此日记

此日记由 Cnangel 发表于 September 10, 2009 11:40 PM

此Blog上的上一篇日记那一片紫色

此Blog上的下一篇日记阿里巴巴十周年庆

首页归档页可以看到最新的日记和所有日记。

归档

Powered by Movable Type 5.14-en