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越开心!

Monthly Archives

Pages

Powered by Movable Type 7.7.2

About this Entry

This page contains a single entry by Cnangel published on September 10, 2009 11:40 PM.

那一片紫色 was the previous entry in this blog.

阿里巴巴十周年庆 is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.