1 前言
XS是Perl与C的胶水语言,通过它能在Perl中创建方法,以此扩展C库中的函数或新定义的C函数,详情可参阅《官方手册:perlxs》。
XS的编译器叫做xsubpp
,它用typemaps去决定如何映射C函数的参量和输出值到Perl的值中并返回。“XSUB结构(XSUB forms)”是XS接口的基本单元,一个XSUB
被编译后等效于一个C函数,其转化过程如下:
- XS从Perl栈中获取参数并转化为C函数期望的格式;
- 调用C函数;
- 将C函数的“输出值”翻译回Perl值。这里的“输出值”指的是C函数的返回值或出参。
- 返回值:通过将返回值放回Perl栈来返回到Perl中
- 出参:直接在Perl侧修改参数值
XSUB
实际上还可以做很多事,比如:
- 检测入参是否有效;
- 抛出异常或返回
undef
或()
; - 基于参数个数或类型而调用不同的C函数;
- 提供一个面向对象的接口等。
1.1 代码规范
注意:这是作者本人在开发过程中总结出来的经验
缩进(单位为空格):
- 章节:2
- 代码:4
- 参数:2
章节与章节之间间隔一个个空行
代码语言:javascript复制function(a)
INPUT:
char *a;
PREINIT:
char *b;
参数列表的名称左对齐,*
贴近名称,&
贴近类型:
function(a, b, c)
char *a;
char b;
char & c;
2 概要
假设有个C接口为:
代码语言:javascript复制bool_t rpcb_gettime(const char *host, time_t *timep);
C函数的使用方法:
代码语言:javascript复制#include <rpc/rpc.h>
bool_t status;
time_t timep;
status = rpcb_gettime("localhost", &timep);
期望在Perl中的调用为:
代码语言:javascript复制use RPC;
$status = rpcb_gettime("localhost", $timep);
那么需要编写XS文件(XSUB)以扩展C中的rpcb_gettime
函数,内容:
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <rpc/rpc.h>
MODULE = RPC PACKAGE = RPC
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
OUTPUT:
timep
当然,任何Perl扩展(即XS)想要插入到Perl中都需要通过Perl模块(.pm
)来引导,因此还需要编写一个.pm
文件:
package RPC;
require Exporter;
require DynaLoader;
@ISA = qw(Exporter DynaLoader);
@EXPORT = qw( rpcb_gettime ); # 导出扩展中的 rpcb_gettime 方法
bootstrap RPC; # 引导一个RPC的扩展
1;
3 剖析XSUB
最简单的XSUB
由3个部分组成:
- 返回值类型
- 函数名
- 参数列表
比如:
代码语言:javascript复制double
sin(x)
double x
上述的XSUB意思是“允许Perl去访问一个同名的C函数sin()
,并将double x
作为其入参传入,同时返回一个double
值给Perl”。
对C函数的指针类型,在XSUB
中有两种表达方式,分别是*
和&
;比如有下面的C函数:
bool string_looks_as_a_number(char *s);
bool make_char_uppercase(char *c);
那么在XSUB
中的参数列表中可以分别表示为:
char *s
char &c
它们都表达着C语言中的指针,当然仍旧有一些细微的差别,在后续“The & Unary Operator“章节中讲述。
在书写格式上要求“返回值类型”、“函数名”和“参数列表”是需要在不同行的,且“返回值类型”与“函数名”需要左对齐,而“参数列表”则既可以保持左对齐,也可以缩进:
缩进(推荐):
代码语言:javascript复制double
sin(x)
double x
不缩进:
代码语言:javascript复制double
sin(x)
double x
在“函数名”后面默认紧跟的参数列表其实是一个默认的章节INPUT:
,在“函数名”之后的内容都由章节(section)声明后定义,比如:
double
sin(x)
PREINIT:
char *host = "127.0.0.1"
INPUT:
double x
如上所示,在声明章节(section)的时候会紧跟着一个冒号(:),并在下面去定义内容。
3.1 参数栈
Perl用参数栈去存储Perl发送给XSUB
的参数,以及XSUB
要返回给Perl的返回值。XSUB
用宏ST(x)
来使用栈,比如在函数中的首个参数可以用ST(0)
表示。无论是参数还是返回值都由ST(x)
表达,并且总是从ST(0)
作为开始,比如有“3个参数”的话,它们分别为ST(0)
、ST(1)
和ST(2)
。
3.2 变量:RETVAL
RETVAL
变量是一个自动声明的特殊C变量。它会自动的匹配C库函数的返回值类型。xsubpp
会为每个有返回值的XSUB
都声明一个这样的变量。在默认情况下,XSUB
创建的C函数会用RETVAL
去存储调用C库函数时得到的返回值。
在简单的情况下,RETVAL
的值会被放在ST(0)
中,最终作为XSUB的返回值被Perl接收。
有两种情况下不会使用到该变量:
- 返回值类型为
void
- 声明了
PPCODE:
3.2.1 通过RETVAL返回SVs, AVs和HVs
如果返回值类型是SV *
的话,会自动的把RETVAL
标识为mortal,即退出函数后自动释放空间。比如下面的写法是等效的:
void
alpha()
PPCODE:
ST(0) = newSVpv("Hello World",0);
sv_2mortal(ST(0));
XSRETURN(1);
SV *
beta()
CODE:
RETVAL = newSVpv("Hello World",0);
OUTPUT:
RETVAL
但是对于AV *
和HV *
来说则不行,这是一个已知但又不能修复的Bug(修复它会导致CPAN模块出现问题),因此对于上面两种情况只能手动调用sv_2mortal
才可以:
AV *
array()
CODE:
RETVAL = newAV();
sv_2mortal((SV*)RETVAL);
/* do something with RETVAL */
OUTPUT:
RETVAL
3.3 关键字:MODULE
MODULE
关键字用来标识XS代码的开始,同时在.pm
文件中指令bootstrap
引导的模块名就是由该指令指定的。如果没有用PACKAGE
关键字设置包名(package
),则默认使用MODULE
的值作为package
。
在首个MODULE
之前的代码都被当成C代码处理,当前如果其中有POD语句的话则会被识别并跳过。
这个指令在相同的XS文件中应当保持不变,仅最后一个MOUDLE
名称有效。
MODULE = RPC
3.4 关键字:PACKAGE
如果想把不同的函数分配给不同的package
则使用此关键字,且该关键字必须跟在MODULE
后面。
相同的PACKAGE
值可以被使用超过一次,这样可以处理非连续情况的代码。通常该指令建议总是存在的,这样能够显示的表示出函数所属的package
。
MODULE = RPC PACKAGE = RPC
[ XS code in package RPC ]
MODULE = RPC PACKAGE = RPCB
[ XS code in package RPCB ]
MODULE = RPC PACKAGE = RPC
[ XS code in package RPC ]
3.5 关键字 PREFIX
PREFIX
必须在PACKAGE
之后,如果没有PACKAGE
的话则必须在MODULE
之后。
MODULE = RPC PREFIX = rpc_
MODULE = RPC PACKAGE = RPCB PREFIX = rpcb_
PREFIX
的功能是用来移除固定前缀,它标注的前缀会在Perl中移除。比如:PREFIX = rpcb_
,则对于rpcb_gettime()
来说,在Perl中的调用则是gettime()
。
XS文件如下:
代码语言:javascript复制MODULE = RPC PACKAGE = RPCB PREFIX = rpcb_
bool_t
rpcb_gettime(host,timep)
char *host
time_t & timep
OUTPUT:
timep
Perl调用如下:
代码语言:javascript复制my $status = gettime($host, $time);
3.6 章节:OUTPUT
OUTPUT:
章节用来指示哪些变量作为输出值返回到Perl中,通常有以下两种用途:
- 指示变量为函数返回值
- 指示参量列表中的变量为出参
在那些没有包含CODE:
或PPCODE:
章节的简单函数中,RETVAL
变量会被自动指示为函数出返回值,而在其它情况下,则需要OUTPUT
去告诉xsubpp
编译器哪些变量要作为输出值返回到Perl中。
bool_t
rpcb_gettime(host,timep)
char *host
time_t & timep
OUTPUT:
timep
上述代码指出timep
作为函数的出参,在Perl中的表现即是更新其变量的值。由于没有CODE:
章节,因此RETVAL
变量默认被作为为函数的返回值定义并使用。
3.7 关键字:NO_OUTPUT
NO_OUTPUT
放置的位置是XSUB
的开头。它表示如果C函数有返回一个非空值的话,应当被忽略。(我们知道C函数的返回值默认会被赋值到RETVAL
变量,如果声明了此关键字,则RETVAL
变量的值会被忽略掉,不会被返回给Perl)
这个关键字的意义在于生成一个更贴合Perl风格的函数,比如:
代码语言:javascript复制NO_OUTPUT int
delete_file(char *name)
POSTCALL:
if (RETVAL != 0)
croak("Error %d while deleting file '%s'", RETVAL, name);
上述代码在Perl中的表现是,成功的时候不会有值返回,但在失败的时候会die。这种风格是贴近Perl风格的,即把一个带有返回值的C函数,改为一个没有返回值但会抛出异常的Perl函数。
3.8 章节:CODE
该章节用于复杂的XSUB
,在章节中写入一些C语句。如果使用了CODE:
章节,RETVAL
不再默认返回,需要显示的在OUTPUT:
章节中指定。
bool_t
rpcb_gettime(host,timep)
char *host
time_t timep
CODE:
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL
3.9 章节:INIT
本章节允许在产生“调用到C函数”之前去做一些初始化动作。但它跟CODE:
章节不同,它不会影响RETVAL
的默认行为。
bool_t
rpcb_gettime(host,timep)
char *host
time_t & timep
INIT:
if (host == NULL) XSRETURN_UNDEF;
OUTPUT:
timep
另一种使用本章节的场景是检查前置条件:
代码语言:javascript复制long
lldiv(a,b)
long long a
long long b
INIT:
if (a == 0 && b == 0)
XSRETURN_UNDEF;
if (b == 0)
croak("lldiv: cannot divide by 0");
3.10 关键字:NO_INIT
本关键字用来指示参量仅作为出参使用。默认情况下,xsubpp
编译器会从参数栈中读取所有参量的值,并将它作为C函数的入参值使用。如果有参量被标记了此关键字,则xsubpp
编译器将忽略它的初始值:
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep = NO_INIT
OUTPUT:
timep
3.11 语法:初始化函数参量
C函数的参量的值通常是从Perl传过来的(XSUB
负责将Perl类型值转化为C类型值)。通过“初始化函数参量”语法,可以去自定义初始化方法。
它的语法有三种,用=
,;
或
紧跟在参量名称后面,作为初始化语句的开头,比如:
char *host = (char *)SvPV($arg,PL_na);
char *host SvOK($v{timep}) ? SvPV($arg,PL_na) : NULL;
char *host ; (char *)SvPV($arg,PL_na);
=
:正常的初始化语句INPUT:
语句执行完后再进行初始化,有滞后执行的效果;
:除了做了host
原本在perl中传进来的值会被忽略掉。
该语法主要是用于如下场景:参量的值必须调用其它库获取
代码语言:javascript复制bool_t
rpcb_gettime(host,timep)
char *host = (char *)SvPV($arg,PL_na);
time_t &timep = 0;
OUTPUT:
timep
此外,该语句在Perl中会自动的被双引号括起来,即可以使用变量,因此如果有任何包含特殊字符$
、@
或的都需要用
进行转义。
3.12 语法:默认参量值
允许指定参量的默认值,可以设置的有效默认值为“数字”、“字符串”或者“NO_INIT”。
比如,用以下语句来设置host
的默认值为"localhost"
:
bool_t
rpcb_gettime(timep,host="localhost")
char *host
time_t timep = NO_INIT
CODE:
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL
则在Perl中可以有以下两种方式调用:
代码语言:javascript复制$status = rpcb_gettime( $timep, $host );
$status = rpcb_gettime( $timep );
3.13 章节:PREINIT
PREINIT:
章节允许在INPUT:
之前或之后添加变量。虽然INIT:
和CODE:
都能添加变量,但是只有PREINIT:
可以在INPUT:
之前添加。并且该章节可以被多次使用。
MyObject
mutate(o)
PREINIT:
MyState st = global_state;
INPUT:
MyObject o;
CLEANUP:
reset_to(global_state, st);
3.14 章节:SCOPE
SCOPE:
章节用来决定某个特定的XSUB
是否允许定义代码范围。代码范围理解起来就好像一堆花括号,花括号中间的临时变量的生命周期仅在花括号之间。当我们使用ENTER和LEAVE的时候就好比是分别填入了一个左括号和右括号。
因此,如果SCOPE:
的值为DISABLE
的话,则无法使用ENTER
和LEAVE
。
SCOPE: ENABLE
代码语言:javascript复制SCOPE: DISABLE
3.15 章节:INPUT
XSUB
的参量通常都是在进入XSUB
之后就立刻转换的,而INPUT:
章节则可以有一些延后。该章节可以被多次使用。
bool_t
rpcb_gettime(host,timep)
PREINIT:
time_t tt;
INPUT:
char *host
PREINIT:
char *h;
INPUT:
time_t timep
CODE:
h = host;
RETVAL = rpcb_gettime( h, &tt );
timep = tt;
OUTPUT:
timep
RETVAL
如上所示,INPUT:章节可以添加非变量,因此也可以这么写:
代码语言:javascript复制bool_t
rpcb_gettime(host,timep)
time_t tt;
char *host
char *h = host;
time_t timep
CODE:
RETVAL = rpcb_gettime( h, &tt );
timep = tt;
OUTPUT:
timep
RETVAL
3.16 关键字:IN/OUTLIST/IN_OUTLIST/OUT/IN_OUT
以上关键字放在参量名前面修饰参量的处理方式。
IN
:代表它是一个Perl入参OUT
:代表它是一个Perl出参OUTLIST
:代表它是一个Perl返回值IN_OUTLIST
:代表它既是入参,又是一个返回值IN_OUT
:代表它既是入参,又是出参
上述除了IN
修饰的参量之外,其它都会当作指针传入到C接口中。
假设有以下C接口:
代码语言:javascript复制void day_month(int *day, int unix_time, int *month);
我们期望如此扩展它为Perl接口:
代码语言:javascript复制my ($day) = day_month(time, $month);
那么可以编写如下XSUB:
代码语言:javascript复制void
day_month(OUTLIST day, IN unix_time, OUT month)
int day
int unix_time
int month
day
是Perl函数的返回值unix_time
是Perl函数的入参month
是Perl函数的出参
它与下面的写法等效:
代码语言:javascript复制void
day_month(day, unix_time, month)
int *day = NO_INIT
int unix_time
int & month = NO_INIT
OUTPUT:
day
month
3.17 函数:length(NAME)
C接口函数对字节或字符串参数很多时候都会需要额外传入一个长度,这跟Perl是不同的(Perl的函数不需要)。有一个便捷的写法来完成这样的转换,比如有一个如下的C接口:
代码语言:javascript复制void dump_chars(char *s, short len);
这时候可以实现如下XSUB
:
void dump_chars(char *s, short length(s))
在Perl中的使用如下:
代码语言:javascript复制dump_chars($string)
3.18 语法:可变参数列表
XSUB
支持可变参数列表,用...
表示,总的参数个数用变量items
保存:
bool_t
rpcb_gettime(timep, ...)
time_t timep = NO_INIT
PREINIT:
char *host = "localhost";
STRLEN n_a;
CODE:
if( items > 1 )
host = (char *)SvPV(ST(1), n_a);
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL
为此我们可以在Perl中如下使用:
代码语言:javascript复制$status = rpcb_gettime( $timep, $host );
# 或者
$status = rpcb_gettime( $timep );
3.19 章节:C_ARGS
C_ARGS:
章节可以定义调用C接口的参数和顺序,这样的好处是在一些情况下可以避免写一个CODE:
或者PPCODE:
章节。比如有如下C接口:
symbolic nth_derivative(int n, symbolic function, int flags);
期望实现的Perl程序:
代码语言:javascript复制$second_deriv = $function->nth_derivative(2);
可以如下实现XSUB
:
symbolic
nth_derivative(function, n)
symbolic function
int n
C_ARGS:
n, function, default_flags
3.20 章节:PPCODE
PPCODE:
跟CODE:
的区别在于:
PPCODE:
自定义返回的参数列表PPCODE:
不会产生RETVAL变量CODE:
会保留入参在堆栈中,并将SP指向末尾。而PPCODE:
则将SP重新指向开头。
用法上,CODE:
用于返回0或1个值。PPCODE:
用于返回2个及以上的值。在PPCODE:
中通过[X]PUSH*()
宏来设置返回值的个数。
void
rpcb_gettime(host)
char *host
PREINIT:
time_t timep;
bool_t status;
PPCODE:
status = rpcb_gettime( host, &timep );
EXTEND(SP, 2); # 增加两个返回值的空间
PUSHs(sv_2mortal(newSViv(status))); # 推入第一个返回值
PUSHs(sv_2mortal(newSViv(timep))); # 推入第二个返回值
如上所示,定义的Perl函数将返回两个值:
代码语言:javascript复制my ($status, $timep) = rpcb_gettime("localhost");
3.21 语法:返回undef和空列表
返回值设置为SV *
,再调用sv_newmortal
去初始化一个返回值(默认值为undef
),或则显示的返回PL_sv_undef
:
SV *
rpcb_gettime(host)
char * host
PREINIT:
time_t timep;
bool_t x;
CODE:
ST(0) = sv_newmortal(); // 初始化返回值,默认值为 undef
if( rpcb_gettime( host, &timep ) )
sv_setnv( ST(0), (double)timep); // 填充返回值
或者显示的设置:
代码语言:javascript复制SV *
rpcb_gettime(host)
char * host
PREINIT:
time_t timep;
bool_t x;
CODE:
if( rpcb_gettime( host, &timep ) )
{
ST(0) = sv_newmortal();
sv_setnv( ST(0), (double)timep);
}
else
{
ST(0) = &PL_sv_undef; // 返回 undef
}
如果想返回空列表必须使用PPCODE:
章节:
void
rpcb_gettime(host)
char *host
PREINIT:
time_t timep;
PPCODE:
if( rpcb_gettime( host, &timep ) )
{
PUSHs(sv_2mortal(newSViv(timep)));
}
else
{
/* Nothing pushed on stack, so an empty
* list is implicitly returned. */
}
还有一些宏可以显示的返回undef
和()
:XSRETURN_UNDEF
和XSRETURN_EMPTY
。如下代码所示将返回undef
:
int
rpcb_gettime(host)
char *host
time_t timep;
POSTCALL:
if (RETVAL == 0)
XSRETURN_UNDEF;
3.22 章节:REQUIRE
该章节用来设置xsubpp
编译器的最小版本号,比如:
REQUIRE: 1.922
3.23 章节:CLEANUP
CLEANUP:
将会作为XSUB
的最后一个语句执行,顾名思义,它就是用来做最后的释放操作的,当在程序退出前被调用。如果使用了CLEANUP:
章节,那么它必须在CODE:
/PPCODE:
或OUTPUT:
章节的后面。
3.24 章节:POSTCALL
POSTCALL:
章节在调用了C函数之后立刻执行,它必须再OUTPUT:
和CLEANUP:
之前。
3.25 章节:BOOT
BOOT:
章节可以插入一些代码在bootstrap
函数中。
3.26 章节:VERSIONCHECK
是否版本检测。它对应于xsubpp
的-versioncheck
和-noversioncheck
选项。该值会覆盖xsubpp
的选项值。如果开启的话,则会尝试去检查当前XS的版本是否跟PM设置的版本是否匹配。
VERSIONCHECK: ENABLE
代码语言:javascript复制VERSIONCHECK: DISABLE
3.27 章节:PROTOTYPES
是否使用Perl函数的原型。它对应于xsubpp
的-prototypes
和-noprototypes
选项。该值会覆盖xsubpp
的选项值。该值可以使用多次,用于开启和关闭不同的部分。如果开启了的话,对应的XSUB
将会使用Perl提供的prototypes
。可以理解为将会根据参数列表来限制函数的入参。
PROTOTYPES: ENABLE
代码语言:javascript复制PROTOTYPES: DISABLE
3.28 章节:PROTOTYPE
自定义Perl函数的原型:
代码语言:javascript复制bool_t
rpcb_gettime(timep, ...)
time_t timep = NO_INIT
PROTOTYPE: $;$
PREINIT:
char *host = "localhost";
STRLEN n_a;
CODE:
if( items > 1 )
host = (char *)SvPV(ST(1), n_a);
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL
当然也支持局部的关闭:
代码语言:javascript复制void
rpcb_gettime_noproto()
PROTOTYPE: DISABLE
...
3.29 章节:ALIAS
ALIAS:
用来设置函数别名,设置方式是一个全称,包括了包名。同时XSUB
会用变量ix
存储当前调用的别名的索引值。比如:
- 调用的是原函数声明的名称,则
ix
为0
- 调用的是别名,则
ix
为别名对应的索引值
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
ALIAS:
FOO::gettime = 1
BAR::getit = 2
INIT:
printf("# ix = %dn", ix );
OUTPUT:
timep
如上所示,设置了两个别名FOO::gettime
和BAR::getit
,他们的索引值分别为1
和2
。如果此时调用的是FOO::gettime
,则ix
的值为1
。
3.30 章节:OVERLOAD
函数重载。
如果重载的是函数的话,必须是3个参数(除了nomethod()
)。如果是重载操作的话,必须确保操作不能有字母,引号,以及空格。
SV *
cmp (lobj, robj, swap)
My_Module_obj lobj
My_Module_obj robj
IV swap
OVERLOAD: cmp <=>
{ /* function defined here */}
3.31 章节:FALLBACK
对OVERLOAD:的补充。可以自动产生缺少的重载操作符。
代码语言:javascript复制FALLBACK: TRUE
3.32 章节:INTERFACE
本章节跟ALIAS:
有些相同的地方,都是可以定义一个额外的声明,但是在实现上却有些不同:
- 本章节定义的
XSUB
不需要switch
语句去做区分,它们将共用同一个XSUB
; - 可以被其他的
XSUB
调用。(此处还有盲点,下文暂不做解释)
比如想要定义4个perl函数multiply
、divide
、add
、subtract
,它们的C接口声明都是symbolic f(symbolic, symbolic);
,那么可以如下定义XSUB
:
symbolic
interface_s_ss(arg1, arg2)
symbolic arg1
symbolic arg2
INTERFACE:
multiply divide
add subtract
3.33 章节:INTERFACE_MACRO
注:本章节暂时不知道如何使用,后续再展开
3.34 章节:INCLUDE
引用其他XS代码。有两种使用方式:
直接引用XS文件;
- 假设有一个文件
rpcb1.xsh
:
bool_t
rpcb_gettime(host, timep)
char *host
time_t & timep
- 定义以下语句来引用上述xs文件:
INCLUDE: rpcb1.xsh
作为命令的方式,将执行结果作为引用的内容。
- 在最后追加pipe(
|
)符号:
INCLUDE: cat rpcb1.xsh |
3.35 章节:CASE
此章节跟C语言中的switch case
有点类似,它允许一个XSUB
中有多个不同的部分去执行特定的行为,最后一个CASE:
相当于C语言中的default
。如果使用了本章节,那么其他所有的XS
关键字都必须被CASE:
包含。可以被switch
的变量有ix
和items
。
long
rpcb_gettime(a,b)
CASE: ix == 1
ALIAS:
x_gettime = 1
INPUT:
# 'a' is timep, 'b' is host
char *b
time_t a = NO_INIT
CODE:
RETVAL = rpcb_gettime( b, &a );
OUTPUT:
a
RETVAL
CASE:
# 'a' is host, 'b' is timep
char *a
time_t &b = NO_INIT
OUTPUT:
b
RETVAL
上述示例在perl中可以如下调用:
代码语言:javascript复制$status = rpcb_gettime($host, $timep);
代码语言:javascript复制$status = x_gettime($timep, $host);
3.36 语法:&
与C 语言的引用类似,它表示将Perl变量转换为指针传入到C函数中,返回时再以变量的形式返回回去。效果与perl的recv
接口类似:
my $n = recv($data, 65536, 0);
print "$datan";
如上所示,$data是一个非引用的perl标量,但是却可以在接口中被修改并回传出来。
它能在INPUT:
章节使用,比如:
bool_t
rpcb_gettime(host, timep)
char *host
time_t & timep
OUTPUT:
timep
上述XSUB
将会以rpcb_gettime(char *host, time_t *timep)
的方式调用C函数。
4 插入POD、注释和预处理器指令
C预处理器指令允许在BOOT:
、PREINIT:
、CODE:
、PPCODE:
、POSTCALL:
、CLEANUP:
章节中使用,也允许在函数外使用。
POD允许在任何地方使用,但必须用=cut
结束POD。
注释以#
开头表示,但之前不能有空格。应避免跟预处理器指令相同,最好的方式就是在预处理器的#
前面面添加空格。
在#else
或#endif
之前,最好加一个空行,用来区分函数本身的内容
如果你的预处理器指令是用来做二选一的话,最好使用以下方式:
代码语言:javascript复制#if ... version1
#else /* ... version2 */
#endif
而不是:
代码语言:javascript复制#if ... version1
#endif
#if ... version2
#endif