三、XS的编写
1,XSUB的结构
XS 接口文件以 .xs 为后缀名,里面定义了 Perl 与 C 之间的接口函数。XSUB 是 XS 接口的基本结构单元,通过 xsubpp 编译后,每个 XSUB 都为相应的 C 函数提供了 Perl 与 C 之间的调用接口,如下所示(兼容c++):
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef __cplusplus
}
#endif
MODULE = test
PACKAGE = test PREFIX = t_
void
hello()
CODE:
printf("Hello, world!\n");
① 其中前三个 #include 声明:EXTERN.h,perl.h 和 XSUB.h 应该始终出现在每个 XS 文件的开头。其后是其他的头文件 #include 声明。使用宏__cplusplus和extern "C"是为了兼容C++的编译;
② MODULE= 定义了该 XS 文件所属的
Perl 模块(.pm),同一个 .xs 文件中所有的
MODULE= 都应该保持一致。每个
MODULE= 之后则是对应的
XSUB 定义,直到文件结束或者下一个
MODULE= 语句;
③ PACKAGE= 定义了该函数所在的 Package,当同一个 .xs 文件需要被划分为多个
Package 时
PACKAGE= 则需要被显式指定。PACKAGE=
应该和
MODULE= 放在一起并紧随其后;
④ PREFIX= 定义了下面函数的前缀,只有使用了前缀的函数才能被外部的perl所直接调用;
XSUB中的函数一般由六个部分组成:
I返回值的定义
II函数名和参数名
III参数类型的定义
IV代码段的实现(包括预处理和初始化等等)
v输入值
VI输出值
2,Perl的变量堆栈和参数
Perl 变量堆栈(argument stack)用于存放发送给 XSUB 的参数值及其返回值。
(1)宏ST
ST(n)
XSUB 可以通过宏 ST(n) 访问该堆栈,其中 ST(0) 为该堆栈的起始地址。
(2)宏SP
EXTEND(SP, num);
宏 SP 代表当前 Perl 堆栈指针,当程序从 XSUB 返回时,处理堆栈上的第num个数据。
(3)RETVAL
变量RETVAL 是一个特殊的C变量,它的类型对应于 C 函数的返回值类型,通常情况下,RETVAL 会作为对应 XSUB 的返回值存放到 Perl 变量堆栈的 ST(0)。
当XS返回值为void类型时,编译器不会为该函数申明RETVAL变量;而当XSUB存在PPCODE:关键字时,不能对 RETVAL 变量进行操作,而应该直接操作对应的 Perl 变量堆栈 ST(x)。
这里关键字CODE:和PPCODE:最大的区别是PPCODE直接操作Perl变量堆栈,而不需要将返回值传给Perl。
(4)PUSHs
直接操作Perl变量堆栈的宏
PUSHs(sv_2mortal(newSViv(an_integer)))
PUSHs(sv_2mortal(newSVuv(an_unsigned_integer)))
PUSHs(sv_2mortal(newSVnv(a_double)))
PUSHs(sv_2mortal(newSVpv("Some
String",0)))
当Perl返回多个值时,可以在PPCODE段使用PUSHs将要返回的值依次压入Perl堆栈:
void
gettime(host)
char *host
PREINIT:
time_t
timep;
bool_t
status;
PPCODE:
status = gettime( host, &timep );
EXTEND(SP, 2);
PUSHs(sv_2mortal(newSViv(status)));
PUSHs(sv_2mortal(newSViv(timep)));
(5)XPUSHs
XPUSHs(SV*)
同PUSHs,只是不需要再使用EXTEND来扩展这个栈,比如你并不知道这个XSUB返回值的数目,可以使用XPUSHs来自动帮助你完成。