您正在从Perl 5.30.0查看此文档的版本。查看最新版本

内容

名称

perlxs-XS语言参考手册

描述

介绍

XS是一种接口描述文件格式,用于在Perl和C代码(或C库)之间创建扩展接口,希望与Perl一起使用。XS接口与库相结合,创建一个新库,然后可以动态加载或静态链接到perl中。XS接口描述是用XS语言编写的,是Perl扩展接口的核心组件。

编写XS之前,请阅读“洞穴”第节。

XSUB公司构成XS接口的基本单元。xsubpp公司编译器,每个XSUB相当于一个C函数定义,它将在Perl调用约定和C调用约定之间提供粘合。

粘合代码从Perl堆栈中提取参数,将这些Perl值转换为C函数所需的格式,调用此C函数,将C函数的返回值传递回Perl。这里的返回值可以是传统的C返回值,也可以是作为输出参数的任何C函数参数。这些返回值可以通过将它们放在Perl堆栈上或通过修改Perl端提供的参数传递回Perl。

以上是对实际发生情况的一种简化看法。由于Perl允许比C更灵活的调用约定,XSUB在实践中可能做得更多,例如检查输入参数的有效性,如果C函数的返回值指示失败,则抛出异常(或返回undef/空列表),根据参数的数量和类型调用不同的C函数,提供面向对象的接口等。

当然,可以直接用C编写这样的粘合代码。然而,这将是一项繁琐的任务,尤其是如果需要为多个C函数编写粘合代码,和/或对Perl堆栈规程和其他类似的奥秘不够熟悉。XS在这里起到了解救作用:人们可以写一个更简明扼要的简写,而不是用长手写这种胶水C代码描述并让XS编译器xsubpp公司处理剩下的。

XS语言允许描述如何使用C例程和如何使用相应的Perl例程之间的映射。它还允许创建直接转换为C代码的Perl例程,这些例程与预先存在的C函数无关。在C接口与Perl接口一致的情况下,XSUB声明几乎与C函数的声明相同(K&R风格)。在这种情况下,还有另一种工具称为氢xs它能够将整个C头文件转换为相应的XS文件,该XS文件将为头文件中描述的函数/宏提供粘合。

XS编译器被调用xsubpp公司此编译器创建了让XSUB操作Perl值所必需的构造,并创建了让Perl调用XSUB所必需的粘合。编译器使用类型图以确定如何将C函数参数和输出值映射到Perl值,然后再映射回来。默认的类型映射(Perl附带)处理许多常见的C类型。还可能需要一个补充类型映射来处理链接库的任何特殊结构和类型。有关类型映射的更多信息,请参阅perlx类型映射.

XS格式的文件以C语言部分开始,直到第一个模块=指令。其他XS指令和XSUB定义可能遵循这一行。文件这一部分中使用的“语言”通常称为XS语言。xsubpp公司识别并跳过POD(参见珍珠贝)在C和XS语言部分中,这允许XS文件包含嵌入式文档。

请参见珀尔克斯图特获取有关整个扩展创建过程的教程。

注意:对于一些扩展,Dave Beazley的SWIG系统可能会为创建扩展粘合代码提供更方便的机制。请参见http://www.swig.org网站/了解更多信息。

在路上

下面的许多示例将集中于创建Perl和ONC+RPC绑定库函数之间的接口。rpcb_gettime()函数用于演示XS语言的许多功能。此函数有两个参数;第一个是输入参数,第二个是输出参数。该函数还返回状态值。

bool_t rpcb_gettime(const char*host,time_t*timep);

从C开始,将使用以下语句调用此函数。

#包括<rpc/rpc.h>bool_t状态;time_t timep;status=rpcb_gettime(“localhost”,&timep);

如果创建一个XSUB来提供此函数和Perl之间的直接转换,那么将通过以下代码从Perl使用此XSUB。$status和$timep变量将包含函数的输出。

使用RPC;$status=rpcb_gettime(“本地主机”,$timep);

下面的XS文件显示了一个XS子例程或XSUB,它演示了rpcb_gettime()函数的一个可能接口。这个XSUB表示C和Perl之间的直接转换,因此即使是从Perl也保留了接口。此XSUB将从Perl调用,用法如上所示。注意,前三个#include语句外部.h,珀尔。小时、和XSUB。小时,将始终出现在XS文件的开头。这一方法和其他方法将在本文件后面进行扩展。A#为定义PERL_NO_GET_CONTEXT公司应该出现以更有效地获取解释器上下文,请参见珍珠胶了解详细信息。

#定义PERL_NO_GET_CONTEXT#包括“EXTERN.h”#包括“perl.h”#包括“XSUB.h”#包括<rpc/rpc.h>模块=RPC包=RPCbool_t(布尔特)rpcb_gettime(主机,时间)char*主机time_t和timep输出:时间

任何对Perl的扩展,包括那些包含XSUB的扩展,都应该有一个Perl模块作为引导程序,将扩展拉入Perl。该模块将把扩展的函数和变量导出到Perl程序,并将扩展的XSUB链接到Perl。以下模块将用于本文档中的大多数示例,并且应该在Perl中与使用命令,如前所示。Perl模块将在本文档后面进行更详细的解释。

包装RPC;要求出口商;需要DynaLoader;@ISA=qw(Exporter DynaLoader);@导出=qw(rpcb_gettime);引导RPC;1;

本文将探讨rpcb_gettime()XSUB的各种接口。XSUB将以不同的顺序获取其参数,或获取不同数量的参数。在每种情况下,XSUB都是Perl和实际的C rpcb_gettime()函数之间的抽象,XSUB必须始终确保使用正确的参数调用实际的rpcb_get()函数。这种抽象将允许程序员为C函数创建一个更像Perl的接口。

XSUB的剖析

最简单的XSUB由3部分组成:返回值的描述、XSUB例程的名称及其参数的名称,以及参数的类型或格式的描述。

下面的XSUB允许Perl程序访问名为sin()的C库函数。XSUB将模拟接受单个参数并返回单个值的C函数。

双重的罪(x)双倍x

或者,可以合并类型描述和参数名称列表,将其重写为

双重的sin(双x)

这使得XSUB看起来类似于ANSI C声明。参数列表后允许使用可选分号,如

双重的sin(双x);

具有C指针类型的参数可以具有不同的语义:具有类似声明的C函数

bool string_looks_a_number(字符*s);bool make_char_uppercase(char*c);

以完全不兼容的方式使用。可以描述这些功能的参数xsubpp公司这样地:

字符*s字符(&c)

这两个XS声明都对应于字符*C类型,但它们具有不同的语义,请参见“一元运算符(&U)”.

可以方便地认为间接操作符*应视为类型和地址运算符的一部分&应视为变量的一部分。请参见perlx类型映射有关在C类型中处理限定符和一元运算符的更多信息。

函数名和返回类型必须放在单独的行中,并应进行左对齐调整。

不正确正确双sin(x)双双x sin(x)双倍x

函数描述的其余部分可以缩进或向左调整。下面的示例显示了一个主体向左调整的函数。本文档中的大多数示例都将缩进正文以提高可读性。

对的双重的罪(x)双倍x

更复杂的XSUB可能包含许多其他部分。XSUB的每个部分都以相应的关键字开头,例如INIT:或CLEANUP:。然而,XSUB的前两行总是包含相同的数据:返回类型的描述、函数及其参数的名称。除非用另一个关键字显式标记,否则紧随其后的内容都被视为INPUT:节。(请参见“输入:关键字”.)

XSUB节将继续,直到找到另一个section-start关键字。

参数堆栈

Perl参数堆栈用于存储作为参数发送给XSUB的值,以及存储XSUB返回值。实际上,所有Perl函数(包括非XSUB函数)都会同时在这个堆栈上保留其值,每个函数在堆栈上的位置都有自己的范围。在本文中,该堆栈上属于活动函数的第一个位置将被称为该函数的位置0。

XSUB使用宏引用其堆栈参数ST(x),其中x个指此XSUB堆栈部分中的位置。XSUB将该函数的位置0称为ST(0)。XSUB的传入参数和传出返回值总是从ST(0)开始。对于许多简单的情况xsubpp公司编译器将通过嵌入类型映射中的代码片段来生成处理参数堆栈所需的代码。在更复杂的情况下,程序员必须提供代码。

RETVAL变量

RETVAL变量是一个特殊的C变量,会自动为您声明。RETVAL的C类型与C库函数的返回类型相匹配。这个xsubpp公司编译器将在每个XSUB中使用非-空隙返回类型。默认情况下,生成的C函数将使用RETVAL保存被调用的C库函数的返回值。在简单的情况下,RETVAL的值将放在参数堆栈的ST(0)中,Perl可以在其中接收它作为XSUB的返回值。

如果XSUB的返回类型为空隙则编译器不会为该函数声明RETVAL变量。使用PPCODE:节时,不需要操作RETVAL变量,该节可以使用直接堆栈操作将输出值放置在堆栈上。

如果未使用PPCODE:指令,空隙返回值应仅用于不返回值的子程序,即使CODE:使用了显式设置ST(0)的指令。

建议使用此文档的旧版本空隙在这种情况下返回值。发现当XSUB真正地 空隙。此做法现在已弃用,将来的某些版本可能不支持。使用返回值SV公司*在这种情况下。(目前xsubpp公司包含一些启发式代码,试图消除“true-void”和“old-practice-declared-as-void”函数之间的歧义。因此,除非您使用SV公司*作为返回值。)

通过RETVAL返回SV、AV和HV

当您使用RETVAL返回SV公司*,幕后有一些神奇的东西值得一提。例如,当您使用ST(x)宏操作参数堆栈时,通常必须特别注意引用计数。(有关参考计数的更多信息,请参阅珍珠胶.)为了让您的生活更轻松,类型映射文件会自动RETVAL公司当你返回一个SV公司*因此,以下两个XSUB或多或少是等效的:

空隙alpha()PPCODE(应用程序代码):ST(0)=newSVpv(“Hello World”,0);sv_2致命(ST(0));XSRETURN(1);SV公司*β()代码:RETVAL=newSVpv(“Hello World”,0);输出:RETVAL公司

这非常有用,因为它通常可以提高可读性。虽然这对SV公司*,不幸的是,它没有那么容易拥有AV(音频/视频)*高压*作为返回值。应该能够书写:

AV(音频/视频)*数组()代码:RETVAL=新AV();/*用RETVAL做点什么*/输出:RETVAL公司

但由于typemap文件中存在一个不可修复的bug(修复它会破坏许多现有CPAN模块)AV(音频/视频)*未正确递减。因此,无论何时调用上述XSUB都会泄漏内存。同样的问题也存在于高压*,简历*、和SVREF公司(表示标量引用,而不是常规引用SV公司*). 在从perl 5.16开始的perls上的XS代码中,可以使用正确处理refcounts的版本覆盖任何这些类型的类型映射。在您的TYPEMAP(类型图)部分,do

AV*T_AVREF_REFCOUNT_FIXED

以获得修复的变体。为了与旧版本的perl向后兼容,您可以在使用以下命令返回上述类型之一时手动减少引用计数sv_致命:

AV(音频/视频)*数组()代码:RETVAL=新AV();sv_2致命((sv*)RETVAL);/*用RETVAL做点什么*/输出:RETVAL公司

记住,你不必为了SV公司*。有关所有核心类型图的参考文档,请参阅perlx类型映射.

MODULE关键字

MODULE关键字用于启动XS代码并指定正在定义的函数的包。第一个MODULE关键字之前的所有文本都被视为C代码,并被传递到输出中,POD被剥离,但在其他方面未被修改。每个XS模块都有一个引导函数,用于将XSUB挂接到Perl中。此引导程序函数的包名称将与XS源文件中最后一条MODULE语句的值匹配。MODULE的值在同一XS文件中应始终保持不变,尽管这不是必需的。

下面的示例将启动XS代码,并将所有函数放在名为RPC的包中。

模块=RPC

PACKAGE关键字

当XS源文件中的函数必须分为包时,应使用PACKAGE关键字。此关键字与MODULE关键字一起使用,使用时必须紧跟其后。

模块=RPC包=RPC[包RPC中的XS代码]模块=RPC包=RPCB[包装RPCB中的XS代码]模块=RPC包=RPC[包RPC中的XS代码]

同一软件包名称可以多次使用,允许非连续代码。如果您的排序原则比包名称更强大,那么这将非常有用。

尽管此关键字是可选的,并且在某些情况下提供了冗余信息,但应始终使用它。此关键字将确保XSUB出现在所需的包中。

PREFIX关键字

PREFIX关键字指定应该从Perl函数名中删除的前缀。如果C函数是rpcb_gettime()PREFIX值为rpcb公司_那么Perl将把这个函数看作获取时间().

使用时,此关键字应跟随PACKAGE关键字。如果未使用PACKAGE,则前缀应跟随MODULE关键字。

模块=RPC前缀=RPC_模块=RPC包=RPCB前缀=RPCB_

输出:关键字

关键字OUTPUT:表示当XSUB终止时,应该更新某些函数参数(新值对Perl可见),或者应该将某些值返回给调用Perl函数。对于没有CODE:或PPCODE:部分的简单函数,例如上面的sin()函数,RETVAL变量会自动指定为输出值。对于更复杂的函数xsubpp公司编译器需要帮助确定哪些变量是输出变量。

此关键字通常用于补充CODE:关键字。当存在CODE:关键字时,RETVAL变量不会被识别为输出变量。在这种情况下,使用OUTPUT:关键字告诉编译器RETVAL实际上是一个输出变量。

关键字OUTPUT:也可以用于指示函数参数是输出变量。当函数中的参数被修改并且程序员希望Perl看到更新时,这可能是必要的。

bool_t(布尔特)rpcb_gettime(主机,时间)char*主机time_t和timep输出:时间

关键字OUTPUT:还允许将输出参数映射到匹配的代码段,而不是映射到类型映射。

bool_t(布尔特)rpcb_gettime(主机,时间)char*主机time_t和timep输出:timep sv_setnv(ST(1),(双)timep);

xsubpp公司发出自动信号SvSETMAGIC()对于XSUB的OUTPUT部分中的所有参数,RETVAL除外。这是通常需要的行为,因为它会正确调用输出参数上的“set”魔法(如果散列或数组元素参数不存在,则必须创建这些参数)。如果出于某种原因,不需要这种行为,则OUTPUT部分可能包含SETMAGIC:禁用行,以针对OUTPUT部分中的其余参数禁用它。同样,SETMAGIC:启用可以用于为OUTPUT部分的剩余部分重新启用它。请参见珍珠胶有关“设置”魔法的更多详细信息。

NO_OUTPUT关键字

NO_OUTPUT可以作为XSUB的第一个标记。此关键字表示,虽然我们为C子例程提供的接口具有非-空隙返回类型,则不应从生成的Perl子例程返回此C子例程的返回值。

存在此关键字“RETVAL变量”在生成的子程序调用中,此变量被赋值,但此变量的值不会在自动生成的代码中使用。

只有在以下情况下,此关键字才有意义RETVAL公司将由用户提供的代码访问。使函数接口更像Perl尤其有用,尤其是当C返回值只是错误条件指示器时。例如,

NO_OUTPUT整数delete_file(字符*名称)海报:if(RETVAL!=0)croak(“删除文件“%s”时出现错误%d”,RETVAL,name);

在这里,生成的XS函数在成功时不返回任何结果,并在出错时显示一条有意义的错误消息()。

代码:关键字

此关键字用于更复杂的XSUB,这些XSUB需要对C函数进行特殊处理。RETVAL变量仍被声明,但除非在OUTPUT:部分中指定,否则不会返回它。

以下XSUB适用于需要对其参数进行特殊处理的C函数。首先给出Perl的用法。

$status=rpcb_gettime(“本地主机”,$timep);

接下来是XSUB。

bool_t(布尔特)rpcb_gettime(主机,时间)char*主机time_t时间代码:RETVAL=rpcb_gettime(主机,&timep);输出:时间RETVAL公司

INIT:关键词

关键字INIT:允许在编译器生成对C函数的调用之前将初始化插入XSUB。与上面的CODE:关键字不同,此关键字不影响编译器处理RETVAL的方式。

bool_t(布尔特)rpcb_gettime(主机,时间)char*主机time_t和timep初始:printf(“#主机是%s\n”,主机);输出:时间

INIT:部分的另一个用途是在调用C函数之前检查先决条件:

长-长lldiv(a,b)long-long长a长-长b初始:如果(a==0&&b==0)XSRETURN_UNDEF;如果(b==0)croak(“lldiv:不能除以0”);

NO_INIT关键字

NO_INIT关键字用于指示函数参数仅用作输出值。这个xsubpp公司编译器通常会生成代码,从参数堆栈中读取所有函数参数的值,并在进入函数时将其分配给C变量。NO_INIT将告诉编译器,某些参数将用于输出而不是输入,并且将在函数终止之前进行处理。

下面的示例显示rpcb_gettime()函数的变体。此函数仅将timep变量用作输出变量,不关心其初始内容。

bool_t(布尔特)rpcb_gettime(主机,时间)char*主机time_t&timep=无初始化输出:时间

TYPEMAP:关键词

从Perl 5.16开始,您可以将类型映射嵌入到XS代码中,而不是将其添加到单独的文件中。多个这样的嵌入类型映射将按XS代码中的显示顺序进行处理,并且像本地类型映射文件一样,它们优先于默认类型映射,嵌入的类型映射可能会覆盖typemap、INPUT和OUTPUT节的先前定义。嵌入式类型映射的语法是

类型映射:<<此处…此处显示您的类型图代码。。。酒店雇员和饭馆雇员

其中TYPEMAP(类型图)关键字必须出现在新行的第一列。

请参阅perlx类型映射有关编写排版图的详细信息。

初始化函数参数

C函数参数通常使用参数堆栈中的值进行初始化(参数堆栈又包含从Perl传递到XSUB的参数)。类型映射包含用于将Perl值转换为C参数的代码段。然而,程序员可以覆盖类型映射并提供备用(或附加)初始化代码。初始化代码以第一个开始=,;+在INPUT:部分的一行上。唯一的例外情况是;终止该行,然后是;被悄悄地忽略了。

以下代码演示了如何为函数参数提供初始化代码。在将初始化代码添加到输出之前,编译器会在双引号内对其进行求值,因此任何应该从字面上解释的内容[主要$,@,或\\]必须用反斜线保护。变量$变量,$参数、和$类型可以在类型图中使用。

bool_t(布尔特)rpcb_gettime(主机,时间)char*主机=(char*)SvPV_nolen($arg);time_t&timep=0;输出:时间

这不应用于提供参数的默认值。当函数参数必须由另一个库函数处理后才能使用时,通常会使用此选项。下一节将介绍默认参数。

如果初始化开始于=,然后在输入变量的声明中输出,替换类型映射提供的初始化。如果初始化开始于;+,然后在声明所有输入变量后执行。;如果未执行类型映射通常提供的初始化。对于+在这种情况下,变量的声明将包括来自类型映射的初始化。全局变量,%v(v)适用于真正罕见的情况,即在另一个初始化中需要来自一个初始化的信息。

下面是一个真正晦涩的例子:

bool_t(布尔特)rpcb_gettime(主机,时间)time_t&timep;/*\$v{timep}=@{[$v{timep}=$arg]}*/char*host+SvOK($v{timep})?SvPV_nolen($arg):空;输出:时间

构造\$v{timep}=@{[$v{timep}=$arg]}在上述示例中使用有两个目的:首先,当该行由xsubpp公司,Perl代码段$v{timep}=$arg评估。其次,计算的代码段的文本被输出到生成的C文件中(在C注释中)!在处理过程中char*主机线路,$参数将评估为ST(0)、和$v{timep}将评估为ST(1).

默认参数值

可以通过在参数列表中放置赋值语句来指定XSUB参数的默认值。默认值可以是数字、字符串或特殊字符串编号(_I)。默认值应始终仅用于最右侧的参数。

为了允许rpcb_gettime()的XSUB具有默认主机值,可以重新排列XSUB的参数。然后,XSUB将使用正确顺序的参数调用实际的rpcb_gettime()函数。可以使用以下语句之一从Perl调用此XSUB:

$status=rpcb_gettime($timep,$host);$status=rpcb_gettime($timep);

XSUB看起来像下面的代码。代码:块用于调用实际的rpcb_gettime()函数,其中参数的顺序与该函数的正确顺序一致。

bool_t(布尔特)rpcb_gettime(timep,host=“localhost”)char*主机time_t timep=无初始化代码:RETVAL=rpcb_gettime(主机,&timep);输出:时间RETVAL公司

PREINIT:关键词

关键字PREINIT:允许在发出INPUT:部分中的参数声明之前或之后立即声明额外的变量。

如果在CODE:节中声明变量,它将跟随为输入参数发出的任何类型映射代码。这可能导致声明在C代码之后结束,这是C语法错误。类似的错误可能会发生在显式;-类型或+-使用参数的类型初始化(请参见“正在初始化函数参数”). 在INIT:节中声明这些变量没有帮助。

在这种情况下,要强制将附加变量与其他变量的声明一起声明,请将声明放入PREINIT:部分。关键字PREINIT:可以在XSUB中使用一次或多次。

以下示例是等效的,但如果代码使用复杂的类型映射,那么第一个示例更安全。

bool_t(布尔特)rpcb_gettime(时间)time_t timep=无初始化前言:char*host=“localhost”;代码:RETVAL=rpcb_gettime(主机,&timep);输出:时间RETVAL公司

对于这种特殊情况,INIT:关键字将生成与PREINIT:关键字相同的C代码。另一个正确但容易出错的示例:

bool_t(布尔特)rpcb_gettime(时间)time_t timep=无初始化代码:char*host=“localhost”;RETVAL=rpcb_gettime(主机,&timep);输出:时间RETVAL公司

另一种申报方式主办在CODE:部分中使用C块:

bool_t(布尔特)rpcb_gettime(时间)time_t timep=无初始化代码:{char*host=“localhost”;RETVAL=rpcb_gettime(主机,&timep);}输出:时间RETVAL公司

当类型映射转换操作某些全局状态时,在处理类型映射条目之前放置附加声明的功能非常方便:

我的对象突变(o)前言:MyState st=全局状态;输入:我的对象o;清理:重置为(全局状态,st);

这里我们假设转换为我的对象在INPUT:部分和MyObject中,处理RETVAL时将修改全局变量全球状态。执行这些转换后,我们将恢复旧值全球状态(例如,为了避免内存泄漏)。

还有另一种方法以简洁换取清晰:INPUT部分允许声明C变量,这些变量不出现在子例程的参数列表中。因此,上述mutate()的代码可以重写为

我的对象突变(o)MyState st=全局状态;我的对象o;清理:重置为(全局状态,st);

rpcb_gettime()的代码可以重写为

bool_t(布尔特)rpcb_gettime(时间)time_t timep=无初始化char*host=“localhost”;ARGS(_ARGS):主机,&timep输出:时间RETVAL公司

范围:关键词

关键字SCOPE:允许为特定XSUB启用作用域。如果启用,XSUB将自动调用ENTER和LEAVE。

为了支持潜在的复杂类型映射,如果XSUB使用的类型映射条目包含如下注释/*范围*/然后将自动为该XSUB启用作用域。

要启用作用域,请执行以下操作:

范围:启用

要禁用作用域,请执行以下操作:

范围:禁用

输入:关键字

XSUB的参数通常在进入XSUB后立即计算。INPUT:关键字可以用来强制稍后对这些参数进行求值。关键字INPUT:可以在XSUB中多次使用,并且可以用于列出一个或多个输入变量。此关键字与PREINIT:关键字一起使用。

以下示例显示了输入参数如何时间可以在PREINIT之后晚些时候进行评估。

bool_t(布尔特)rpcb_gettime(主机,时间)char*主机前言:时间t tt;输入:time_t时间代码:RETVAL=rpcb_gettime(主机,&tt);时间p=tt;输出:时间RETVAL公司

下一个示例显示了稍后评估的每个输入参数。

bool_t(布尔特)rpcb_gettime(主机,时间)前言:时间t tt;输入:char*主机前言:字符*h;输入:time_t时间代码:h=主机;RETVAL=rpcb_gettime(h,&tt);时间p=tt;输出:时间RETVAL公司

由于INPUT部分允许声明未出现在子程序参数列表中的C变量,因此可以缩写为:

bool_t(布尔特)rpcb_gettime(主机,时间)时间t tt;char*主机;char*h=主机;time_t timep;代码:RETVAL=rpcb_gettime(h,&tt);时间p=tt;输出:时间RETVAL公司

(我们利用我们的知识字符*是“简单”的,因此主办在声明行上初始化,并且我们的赋值h=主机没有过早执行。否则需要分配任务h=主机在CODE:或INIT:节中。)

IN/OUTLIST/IN_OUTLIST/OUT/IN_OUT关键字

在XSUB的参数列表中,可以用英寸/OUTLIST(输出列表)/输入/输出列表/OUT(输出)/输入/输出关键字。英寸关键字是默认值,其他关键字指示Perl接口与C接口的区别。

前面的参数OUTLIST(输出列表)/输入/输出列表/OUT(输出)/输入/输出关键字被认为是由C子程序使用的通过指针.OUTLIST(输出列表)/OUT(输出)关键字表示C子例程不检查该参数所指向的内存,而是通过该指针写入以提供其他返回值。

前面的参数OUTLIST(输出列表)关键字不会出现在生成的Perl函数的使用签名中。

前面的参数输入/输出列表/输入/输出/OUT(输出) 显示为Perl函数的参数。除了OUT(输出)-参数,这些参数被转换为相应的C类型,然后指向这些数据的指针作为C函数的参数。预计C函数将通过这些指针进行写入。

生成的Perl函数的返回列表由函数的C返回值组成(除非XSUB为空隙返回类型或NO_OUTPUT关键字使用),然后是所有OUTLIST(输出列表)输入/输出列表参数(按外观顺序)。从XSUB返回时输入/输出/OUT(输出)Perl参数将被修改为由C函数写入值。

例如,XSUB

空隙day_month(OUTLIST天,IN unix_time,OUTLIST-month)int天int unix时间int个月

应在Perl中用作

my($day,$month)=日_月(time);

相应函数的C签名应为

void day_month(int*day,intunix_time,int*month);

这个英寸/OUTLIST(输出列表)/输入/输出列表/输入/输出/OUT(输出)关键字可以与ANSI样式声明混合,如

空隙day_month(OUTLIST int day,int unix_time,OUTLIST-int month)

(这里是可选的英寸关键字被省略)。

这个输入/输出参数与引入的参数相同“一元运算符(&U)”并将其放入输出:部分(参见“输出:关键字”). 这个输入/输出列表参数非常相似,唯一的区别是C函数通过指针写入的值不会修改Perl参数,而是放在输出列表中。

这个OUTLIST(输出列表)/OUT(输出)参数不同于输入/输出列表/输入/输出参数仅由未被读取的Perl参数的初始值决定(并且未被赋予C函数-它会得到一些垃圾)。例如,与上述相同的C函数可以作为

void day_month(OUT int day,int unix_time,OUT int month);

空隙day_month(日,unix_time,月)int&day=NO_INITint unix时间int&month=NO_INIT输出:白天

然而,生成的Perl函数是以非常C-ish的方式调用的:

我的($day,$month);day_month($day,time,$month);

这个长度(名称)关键字

如果C函数的输入参数之一是字符串参数的长度名称,可以将长度参数的名称替换为长度(名称)在XSUB声明中。调用生成的Perl函数时,必须省略此参数。例如。,

空隙dump_chars(字符*s,短l){短n=0;而(n<l){打印f(“s[%d]=\”\\%#03o\“\n”,n,(int)s[n]);n++;}}模块=x包装=xvoid dump_chars(字符*s,短长度)

应称为dump_chars($string).

仅ANSI类型函数声明支持此指令。

可变长度参数列表

通过指定省略号,XSUB可以具有可变长度的参数列表(...)在参数列表中。省略号的这种用法类似于ANSI C中的省略号。程序员可以通过检查项目变量,其中xsubpp公司编译器为所有XSUB提供。通过使用此机制,可以创建一个XSUB,它接受未知长度的参数列表。

这个主办rpcb_gettime()XSUB的参数可以是可选的,因此可以使用省略号来指示XSUB将接受数量可变的参数。Perl应该能够使用以下语句调用此XSUB。

$status=rpcb_gettime($timep,$host);$status=rpcb_gettime($timep);

下面是带有省略号的XS代码。

bool_t(布尔特)rpcb_gettime(时间,…)time_t timep=无初始化前言:char*host=“localhost”;代码:if(项>1)主机=(char*)SvPV_nolen(ST(1));RETVAL=rpcb_gettime(主机,&timep);输出:时间RETVAL公司

C_ARGS:关键字

C_ARGS:关键字允许创建XSUBS,它在Perl中的调用顺序与在C中的调用序列不同,而无需编写CODE:或PPCODE:节。C_ARGS:paragraph的内容作为被调用C函数的参数,没有任何更改。

例如,假设C函数声明为

符号nth_divative(int n,符号函数,int标志);

默认标志保存在全局C变量中默认标志(_F)。假设您要创建一个称为

$second_deriv=$function->nth_derivative(2);

为此,将XSUB声明为

象征的第n个导数(函数,n)符号函数整数nARGS(_ARGS):n、 函数,default_flags

PPCODE:关键字

PPCODE:关键字是CODE:关键词的另一种形式,用于告知xsubpp公司编译器,程序员提供代码来控制XSUB返回值的参数堆栈。有时人们会希望XSUB返回值列表,而不是单个值。在这些情况下,必须使用PPCODE:,然后显式推送堆栈上的值列表。PPCODE:和CODE:关键字不应在同一XSUB中一起使用。

PPCODE:和CODE:部分之间的实际差异在于初始化服务提供商宏(表示现在的Perl堆栈指针),并在从XSUB返回时处理堆栈上的数据。在CODE:段中,SP保留XSUB入口的值:SP位于函数指针上(紧跟在最后一个参数之后)。在PPCODE中:部分SP向后移动到参数列表的开头,这允许推*()当XSUB返回到Perl时,宏将输出值放置在Perl期望的位置。

为CODE:部分生成的尾部确保Perl将看到的返回值的数量为0或1(取决于空隙C函数返回值的性质,以及中提到的启发式“RETVAL变量”). 为PPCODE:部分生成的尾部基于返回值的数量和次数服务提供商由更新[十] 推*()宏。

请注意,宏ST(i),XST_m*()XSRETURN*()在CODE:sections和PPCODE:sections中同样出色。

以下XSUB将调用C rpcb_gettime()函数,并将其两个输出值timep和status作为单个列表返回给Perl。

空隙rpcb_gettime(主机)char*主机前言:time_t timep;bool_t状态;PPCODE(应用程序代码):status=rpcb_gettime(主机,&timep);延伸(SP,2);推送(sv_2moral(newSViv(状态)));PUSH(sv_2manual(newSViv(timep)));

请注意,程序员必须提供调用真正的rpcb_gettime()函数以及将返回值正确放置在参数堆栈上所必需的C代码。

这个空隙此函数的返回类型告诉xsubpp公司编译器声明不需要或使用RETVAL变量,并且不应创建该变量。在大多数情况下,void返回类型应与PPCODE:指令一起使用。

EXTEND()宏用于在参数堆栈上为2个返回值腾出空间。PPCODE:指令导致xsubpp公司编译器创建可用的堆栈指针服务提供商,并且该指针正在EXTEND()宏中使用。然后使用PUSHs()宏将值推送到堆栈上。

现在,可以通过以下语句从Perl中使用rpcb_gettime()函数。

($status,$timep)=rpcb_gettime(“本地主机”);

使用PPCODE节处理输出参数时,请确保正确处理“设置”魔法。请参见珍珠胶有关“设置”魔法的详细信息。

返回取消定义和空列表

有时程序员会想简单地返回未定义如果函数失败,则为空列表,而不是单独的状态值。rpcb_gettime()函数正好提供了这种情况。如果函数成功,我们希望它返回时间,如果失败,我们希望返回undef。在下面的Perl代码中,$timep的值要么是undef,要么是有效时间。

$timep=rpcb_gettime(“本地主机”);

以下XSUB使用SV公司*返回类型仅作为助记符,并使用CODE:块向编译器指示程序员已提供了所有必要的代码。sv_newmotal()调用将把返回值初始化为undef,使其成为默认返回值。

SV公司*rpcb_gettime(主机)char*主机前言:time_t timep;bool_t x;代码:ST(0)=sv_newmotal();if(rpcbgettime(主机,&timep))sv_setnv(ST(0),(双)timep);

下一个示例演示了如果需要,如何在返回值中放置显式undef。

SV公司*rpcb_gettime(主机)char*主机前言:time_t timep;bool_t x;代码:if(rpcbgettime(主机,&timep)){ST(0)=sv_newmotal();sv_setnv(ST(0),(双)timep);}其他{ST(0)=&PL_sv_undef;}

要返回空列表,必须使用PPCODE:块,然后不要将返回值推送到堆栈上。

空隙rpcb_gettime(主机)char*主机前言:time_t timep;PPCODE(应用程序代码):if(rpcbgettime(主机,&timep))PUSH(sv_2manual(newSViv(timep)));其他{/*堆栈上没有任何内容,因此为空*列表被隐式返回*/}

有些人可能倾向于包含一个明确的返回在上面的XSUB中,而不是让控制权落到最后。在这些情况下XSRETURN_EMPTY(返回_ MPTY)应该使用。这将确保正确调整XSUB堆栈。咨询珀拉皮对于其他XSRETURN(返回)宏。

XSRETURN(返回)_*宏也可以与CODE块一起使用,可以将此示例重写为:

整数rpcb_gettime(主机)char*主机前言:time_t timep;代码:RETVAL=rpcb_gettime(主机,&timep);如果(RETVAL==0)XSRETURN_UNDEF;输出:RETVAL公司

事实上,也可以将此检查放入POSTCALL:部分。再加上PREINIT:简化,这将导致:

整数rpcb_gettime(主机)char*主机time_t timep;海报:如果(RETVAL==0)XSRETURN_UNDEF;

要求:关键字

REQUIRE:关键字用于指示xsubpp公司编译器需要编译XS模块。包含以下语句的XS模块将只使用xsubpp公司1.922或更高版本:

要求:1.922

清洁:关键词

当XSUB在终止之前需要特殊的清理过程时,可以使用此关键字。当使用CLEANUP:关键字时,它必须跟在XSUB中存在的任何CODE:或OUTPUT:块之后。为清理块指定的代码将作为XSUB中的最后一条语句添加。

POSTCALL:关键词

当XSUB需要在执行C子例程调用后执行特殊过程时,可以使用此关键字。当使用POSTCALL:关键字时,它必须位于XSUB中存在的OUTPUT:和CLEANUP:块之前。

请参阅中的示例“NO_OUTPUT关键字”“返回取消定义和空列表”.

当用户通过提供CODE:或PPCODE:部分来提供C子例程调用时,POSTCALL:块没有多大意义。

BOOT:关键词

BOOT:关键字用于将代码添加到扩展的引导程序函数中。引导程序函数由xsubpp公司编译器,通常保存用Perl注册任何XSUB所需的语句。使用BOOT:关键字,程序员可以告诉编译器向引导函数添加额外的语句。

此关键字可以在第一个MODULE关键字之后的任何时间使用,并且应该单独出现在一行上。关键字后的第一个空行将终止代码块。

启动:#当#执行引导程序函数。printf(“您好,来自引导程序!\n”);

VERSIONCHECK:关键字

VERSIONCHECK:关键字对应于xsubpp公司-版本检查-小说检查选项。此关键字将覆盖命令行选项。默认情况下启用版本检查。启用版本检查后,XS模块将尝试验证其版本是否与PM模块的版本匹配。

要启用版本检查,请执行以下操作:

版本检查:启用

要禁用版本检查,请执行以下操作:

版本检查:禁用

请注意,如果PM模块的版本是NV(浮点数),那么它将被字符串化,可能会丢失精度(目前被截断为九位小数),因此它可能不再与XS模块的版本匹配。如果使用长版本号,建议引用$VERSION声明使其成为字符串。

原型:关键字

PROTOTYPES:关键字对应于xsubpp公司-原型-无原型选项。此关键字将覆盖命令行选项。默认情况下禁用原型。当原型被启用时,XSUB将得到Perl原型。此关键字可以在XS模块中多次使用,以启用和禁用模块不同部分的原型。请注意xsubpp公司如果您没有显式启用或禁用原型,会唠叨您:

请指定Foo.xs的原型行为(请参阅perlxs手册)

要启用原型,请执行以下操作:

原型:启用

要禁用原型,请执行以下操作:

原型:禁用

原型:关键字

此关键字与上面的PROTOTYPES:关键字类似,但可以用于强制xsubpp公司为XSUB使用特定的原型。此关键字覆盖所有其他原型选项和关键字,但仅影响当前XSUB。咨询perlsub中的“原型”有关Perl原型的信息。

bool_t(布尔特)rpcb_gettime(时间,…)time_t timep=无初始化原型:$$前言:char*host=“localhost”;代码:if(项>1)主机=(char*)SvPV_nolen(ST(1));RETVAL=rpcb_gettime(主机,&timep);输出:时间RETVAL公司

如果启用了原型,则可以在本地为给定的XSUB禁用它,如下例所示:

空隙rpcb_gettime_noproto()原型:禁用...

ALIAS:关键词

关键字ALIAS:允许XSUB有两个或多个唯一的Perl名称,并知道调用时使用了哪些名称。Perl名称可以完全限定为包名称。每个别名都有一个索引。编译器将设置一个名为其中包含使用的别名的索引。使用声明的名称调用XSUB时将为0。

以下示例将创建别名FOO::gettime()酒吧::getit()用于此函数。

bool_t(布尔特)rpcb_gettime(主机,时间)char*主机time_t和timep别名:FOO::gettime=1条形图::getit=2初始:printf(“#ix=%d\n”,ix);输出:时间

OVERLOAD:关键词

您也可以使用OVERLOAD关键字为函数定义其他Perl名称,而不是使用纯Perl编写重载接口(如上面的ALIAS:关键字)。然而,重载函数的定义方式必须能够接受perl重载系统提供的参数数量。对于大多数重载方法,它将是三个参数;对于命名法函数它将是四个。然而,按位运算符&,|,^、和~可以用三个五个参数(请参见超载).

如果任何函数都有OVERLOAD:关键字,那么将在xsubpp生成的c文件中定义多行,以便使用重载魔法进行注册。

由于受祝福的对象实际上存储为RV,因此使用类型映射功能预处理参数并提取受祝福RV中存储的实际SV非常有用。请参阅下面的T_PTROBJ_SPECIAL示例。

要使用OVERLOAD:关键字,请创建一个XS函数,该函数接受三个输入参数(或使用C样式的“…”定义),如下所示:

SV公司*cmp(lobj、robj、swap)我的模块对象lobj我的模块对象IV掉期过载:cmp<=>{/*此处定义的函数*/}

在这种情况下,该函数将重载这两个三向比较运算符。对于所有使用非字母字符的重载操作,必须键入参数而不加引号,并用空格分隔多个重载。请注意,“”(字符串重载)应输入为“\”(即转义)。

如上所述,由于按位运算符可能需要额外的参数,您可能希望使用以下内容(lobj、robj、swap…)(带文字...)作为参数列表。

失败:关键词

除了OVERLOAD关键字外,如果需要控制Perl如何自动生成缺少的重载运算符,可以在模块头部分中设置FALLBACK关键字,如下所示:

模块=RPC包=RPC倒退:正确...

其中FALLBACK可以取TRUE、FALSE或UNDEF这三个值中的任何一个。如果在使用OVERLOAD时未设置任何FALLBACK值,则默认为UNDEF。除非定义了一个或多个使用OVERLOAD的函数,否则不使用FALLBACK。请参阅过载时的“回退”了解更多详细信息。

界面:关键词

此关键字将当前XSUB声明为给定调用签名的守护者。如果此关键字后面有一些文本,则将其视为具有此签名的函数列表,并应附加到当前XSUB。

例如,如果有4个C函数multiply()、divide()、add()和subtract(),它们都有签名:

符号f(符号,符号);

您可以使用以下命令使它们都使用相同的XSUB:

象征的接口(arg1、arg2)符号arg1符号arg2接口:乘除法加减

(这是4个Perl函数的完整XSUB代码!)四个生成的Perl函数与相应的C函数共享名称。

与ALIAS:keyword相比,这种方法的优点是不需要编写switch语句,每个Perl函数(共享相同的XSUB)都知道应该调用哪个C函数。此外,可以在运行时使用以下命令附加额外的函数remainder()

CV*mycv=newXSproto(“符号::剩余”,XS_Symbolic_interface_s_ss,__FILE__,“$$”);XSINTERFACE_FUNC_SET(mycv,余数);

比如,从另一个XSUB。(本例假设没有INTERFACE_MACRO:节,否则需要使用其他内容,而不是XSINTERFACE_FUNC_SET(XS接口_功能_设置),请参阅下一节。)

INTERFACE_MACRO:关键字

此关键字允许使用不同的方法定义INTERFACE,以从XSUB提取函数指针。该关键字后面的文本应给出宏的名称,该宏将提取/设置函数指针。提取器宏为返回类型,简历*、和XSANY.any_dptr公司为了这个简历*。setter宏被赋予cv,函数指针被赋予。

默认值为XS接口_FUNCXSINTERFACE_FUNC_SET(XS接口_功能_设置)。如果使用INTERFACE_MACRO关键字,则可以省略函数列表为空的INTERFACE关键字。

假设在前面的示例函数中,multiply()、divide()、add()和subtract()的指针保存在一个全局C数组中fp[]偏移为乘数(multiply _off),分隔(_O),添加_关闭,减_关。然后可以使用

#定义XSINTERFACE_FUNC_BYOFFSET(ret,cv,f)\((XSINTERFACE_CVT_ANON(ret))fp[CvXSUBANY(cv).any_i32])#定义XSINTERFACE_FUNC_BYOFFSET_set(cv,f)\CvXSUBANY(cv).any_i32=类别2(f,_off)

在C段中,

象征的接口(arg1、arg2)符号arg1符号arg2接口_MACRO:XSINTERFACE_FUNC_BYOFFSET(XSINTERFACE_FUNC_BYOFFSET)XSINTERFACE_FUNC_BYOFFSET设置接口:乘除法加减

在XSUB部分中。

INCLUDE:关键字

此关键字可用于将其他文件拉入XS模块。其他文件可能包含XS代码。INCLUDE:还可用于运行命令,以生成要拉入模块的XS代码。

文件卢比1.xsh包含我们的rpcb_gettime()功能:

bool_t(布尔特)rpcb_gettime(主机,时间)char*主机time_t和timep输出:时间

XS模块可以使用INCLUDE:将该文件拉入其中。

包括:Rpcb1.xsh

如果INCLUDE:关键字的参数后面跟管道(|)然后编译器将参数解释为命令。此功能有点被否决,支持INCLUDE_COMMAND(包含_命令):指令,如下所述。

包括:猫Rpcb1.xsh|

不要使用此选项运行perl:包括:perl|将运行路径中的第一个perl,而不一定是用于运行的同一个perlxsubpp公司。请参阅“INCLUDE_COMMAND:关键字”.

INCLUDE_COMMAND:关键字

运行提供的命令并将其输出包含到当前XS文档中。包括_命令赋予$^X(美元)标记,因为它运行的是正在运行的同一个perl解释器xsubpp公司:

INCLUDE_COMMAND:目录Rpcb1.xshINCLUDE_COMMAND:$^X-e。。。

案例:关键词

CASE:关键字允许XSUB具有多个不同的部分,每个部分充当一个虚拟XSUB。CASE:是贪婪的,如果使用它,则所有其他XS关键字必须包含在CASE:中。这意味着在XSUB中,第一个CASE:之前可能没有任何内容,最后一个CASE:之后的任何内容都包含在该案例中。

CASE:可以通过XSUB的参数,通过ALIAS:变量(请参见“ALIAS:关键词”),或者可能通过项目变量(请参见“可变长度参数列表”). 最后一个案例:成为违约如果它与条件无关,则为case。以下示例显示CASE通过带有一个函数rpcb_gettime()有别名的x_gettime()。当函数被调用为rpcb_gettime()它的参数是通常的(char*主机,time_t*timep),但当函数被调用为x_gettime()参数颠倒,(time_t*timep,char*host).

长的rpcb获取时间(a,b)案例:ix==1别名:x_gettime=1输入:#“a”是timep,“b”是host字符*btime_t a=无初始化代码:RETVAL=rpcb_gettime(b,&a);输出:RETVAL公司案例:#“a”是主机,“b”是时间字符*atime_t&b=无初始化输出:b条RETVAL公司

可以使用以下语句调用该函数。注意不同的参数列表。

$status=rpcb_gettime($host,$timep);$status=x_gettime($timep,$host);

EXPORT_XSUB_SYMBOLS:关键字

EXPORT_XSUB_SYMBOLS:关键字可能是您永远不需要的。在5.16.0之前的perl版本中,此关键字不起任何作用。从5.16开始,默认情况下不再导出XSUB符号。也就是说,他们是静止的功能。如果您包括

EXPORT_XSUB_SYMBOLS:启用

在XS代码中,不会声明此行后面的XSUB静止的。您可以稍后使用禁用此功能

EXPORT_XSUB_SYMBOLS:禁用

这也是默认的,你可能永远不应该改变。您不能在5.16之前的perl版本上使用此关键字来生成XSUB静止的.

一元运算符(&U)

这个&INPUT:部分中的一元运算符用于告诉xsubpp公司它应该使用左边的C类型将Perl值转换为C或从C转换&,但在调用C函数时提供指向此值的指针。

对于通过引用获取参数的C函数,这有助于避免出现CODE:块。通常,参数不应该是指针类型(整数长的但不是整数*长的,长的*).

以下XSUB将生成错误的C代码。这个xsubpp公司编译器会将其转换为调用rpcb_gettime()带参数(char*主机,time_t timep),但真实的rpcb_gettime()想要时间参数的类型time_t(时间)*而不是time_t(时间).

bool_t(布尔特)rpcb_gettime(主机,时间)char*主机time_t时间输出:时间

通过使用&操作员。这个xsubpp公司编译器现在将其转换为调用rpcb_gettime()正确使用参数(char*主机,time_t*timep)。它通过携带&通过,所以函数调用看起来像rpcb_gettime(主机,&timep).

bool_t(布尔特)rpcb_gettime(主机,时间)char*主机time_t和timep输出:时间

插入POD、注释和C预处理器指令

允许在BOOT:、PREINIT:INIT:、CODE:、PPCODE:,POSTCALL:和CLEANUP:块内以及函数外使用C预处理器指令。允许在MODULE关键字之后的任何位置添加注释。编译器将原封不动地传递预处理器指令,并删除注释行。POD文档在任何时候都是允许的,无论是在C语言部分还是在XS语言部分。POD必须以=切割命令;xsubpp公司如果没有,将退出并返回错误。人工生成的C代码不太可能被误认为POD,因为大多数缩进样式都会导致以开头的任何行前面出现空白=。计算机生成的XS文件可能会落入此陷阱,除非注意确保空格打断序列“\n=”。

可以通过放置#作为行的第一个非空白。应注意避免使注释看起来像C预处理器指令,以免被解释为C预处理器指示。防止这种情况发生的最简单方法是在#.

如果使用预处理器指令选择函数的两个版本之一,请使用

#如果。。。版本1#其他/*。。。版本2*/#结尾

而不是

#如果。。。版本1#结尾#如果。。。版本2#结尾

因为不然xsubpp公司会相信你对函数做了重复的定义。此外,在#else/#endif之前放一个空行,这样它就不会被视为函数体的一部分。

将XS与C一起使用++

如果XSUB名称包含::,它被视为C++方法。生成的Perl函数将假定其第一个参数是对象指针。对象指针将存储在名为THIS的变量中。该对象应该是由C++使用new()函数创建的,并且应该是由Perl使用sv_setref_pv()宏创建的。Perl对对象的祝福可以通过类型映射来处理。本节末尾显示了一个类型图示例。

如果XSUB的返回类型包括静止的,该方法被视为静态方法。它将使用class::method()语法调用C++函数。如果该方法不是静态的,则将使用THIS->method()语法调用该函数。

接下来的例子将使用下面的C++类。

类颜色{公众:颜色();~颜色();int蓝色();void set_blue(int);私人:int c_blue;};

blue()和set_blue()方法的XSUB是用类名定义的,但对象的参数(THIS或“self”)是隐式的,未列出。

整数颜色::蓝色()空隙颜色::set_blue(val)整数val

这两个Perl函数都需要一个对象作为第一个参数。在生成的C++代码中,对象被调用这个,将对此对象执行方法调用。因此,在C++代码中,blue()和set_blue(

RETVAL=此->蓝色();此->设置蓝色(val);

您还可以使用可选参数编写单个get/set方法:

整数颜色::蓝色(val=NO_INIT)整数val原型$$代码:if(项>1)此->设置蓝色(val);RETVAL=此->蓝色();输出:RETVAL公司

如果函数的名称为毁灭然后是C++删除函数将被调用,并且这个将作为其参数给出。为生成的C++代码

空隙颜色::DESTROY()

将如下所示:

color*THIS=…;//在类型映射中初始化为删除THIS;

如果函数的名称为新的然后是C++新的函数将被调用以创建动态C++对象。XSUB需要类名,该类名将保存在一个名为类别,作为第一个参数。

颜色*颜色::new()

生成的C++代码将调用新的.

RETVAL=新颜色();

下面是可用于此C++示例的类型映射示例。

TYPEMAP(类型图)颜色*O_OBJECT输出#Perl对象被命名为“CLASS”,它应该是一个#有祝福包裹的名字。对象(_O)sv_setref_pv($arg,类,(void*)$var);INPUT(输入)对象(_O)if(sv_isobject($arg)&&(SvTYPE(SvRV($ag))==SVt_PVMG))$var=($type)SvIV((SV*)SvRV($arg));其他{警告(“${Package}::$func_name()--”。“$var不是受欢迎的SV引用”);XSRETURN_UNDEF;}

接口策略

在设计Perl和C库之间的接口时,需要将C直接转换为XS(例如由h2xs-x型)通常就足够了。然而,有时接口看起来很像C,有时也不直观,尤其是当C函数修改其参数之一或返回带内故障时(如“负返回值表示故障”)。在程序员希望创建更像Perl的接口的情况下,以下策略可能有助于识别接口中更关键的部分。

使用输入/输出或输出参数识别C功能。这些函数的XSUB可以将列表返回给Perl。

识别使用一些带内信息作为故障指示的C功能。它们可能是在失败时返回undef或空列表的候选项。如果在不调用C函数的情况下检测到故障,您可能需要使用INIT:部分来报告故障。对于C函数返回后可检测到的故障,可能需要使用POSTCALL:节来处理故障。在更复杂的情况下,使用CODE:或PPCODE:部分。

如果许多函数基于返回值使用相同的失败指示,您可能需要创建一个特殊的typedef来处理这种情况。放置

typedef int negative_is_failure;类型定义int negative_is_failure;

在XS文件开头附近,并为创建OUTPUT类型映射条目否定是失败将负值转换为未定义,或者可能是croak()s。在此之后,类型的返回值否定是失败将创建更多类似Perl的界面。

确定哪些值仅由C和XSUB函数本身使用,例如,当函数的参数应该是全局变量的内容时。如果Perl不需要访问值的内容,那么可能不需要为该值提供从C到Perl的转换。

识别C函数参数列表中的指针和返回值。一些指针可以用来实现输入/输出或输出参数,它们可以在XS中用&一元运算符,并且可能使用NO_INIT关键字。其他一些则需要处理以下类型整数*在这种情况下,需要决定一个有用的Perl翻译会做什么。当语义明确时,建议将译文放入类型映射文件中。

识别C函数使用的结构。在许多情况下,为这些结构使用T_PTROBJ类型映射可能会有所帮助,以便Perl可以将它们作为受祝福的对象进行操作。(这由自动处理h2xs-x型.)

如果在需要不同翻译的几个不同上下文中使用相同的C类型,类型定义映射到此C类型的几个新类型,并创建单独的类型图这些新类型的条目。在XSUB的返回类型和参数声明中使用这些类型。

Perl对象和C结构

在处理C结构时,应该选择其中之一T_PTROBJ公司T_PTRREF(_PTRREF)用于XS类型。这两种类型都设计用于处理指向复杂对象的指针。T_PTRREF类型将允许取消对Perl对象的访问,而T_PTROBJ类型要求对该对象进行访问。通过使用T_PTROBJ,可以实现一种类型检查形式,因为XSUB将尝试验证Perl对象是否为预期类型。

以下XS代码显示了与ONC+TIRPC一起使用的getnetconfigent()函数。getnetconfigent()函数将返回一个指向C结构的指针,并具有如下所示的C原型。该示例将演示C指针将如何成为Perl引用。Perl会将此引用视为指向受祝福对象的指针,并尝试为该对象调用析构函数。XS源中将提供一个析构函数来释放getnetconfig()使用的内存。XS中的析构函数可以通过指定名称以单词结尾的XSUB函数来创建毁灭.XS析构函数可以用来释放可能被另一个XSUB破坏的内存。

struct netconfig*getnetconfigent(常量字符*netid);

A类类型定义将为创建结构netconfig。Perl对象将在一个与C类型名称匹配的类中使用标记Ptr公司如果名称是Perl包名称,则该名称不应包含嵌入空格。析构函数将被放置在与对象类相对应的类中,PREFIX关键字将被用于按照Perl的预期将名称修饰为单词DESTROY。

typedef结构netconfig netconfig;模块=RPC包=RPCNetconfig(网络配置)*getnetconfigent(网络ID)字符*netid模块=RPC包=NetconfigPtr前缀=rpcb_空隙rpcb_DESTROY(网络连接)Netconfig*netconf代码:printf(“现在在NetconfigPtr::DESTROY\n”);免费(netconf);

此示例需要以下类型映射条目。咨询perlx类型映射有关为扩展添加新类型映射的更多信息。

TYPEMAP(类型图)网络配置*T_PTROBJ

此示例将与以下Perl语句一起使用。

使用RPC;$netconf=getnetconfigent(“udp”);

当Perl销毁$netconf引用的对象时,它会将该对象发送到提供的XSUB DESTROY函数。Perl无法确定,也不关心这个对象是否是C结构,而不是Perl对象。从这个意义上讲,getnetconfigent()XSUB创建的对象与普通Perl子例程创建的对象没有区别。

在XS中安全存储静态数据

从Perl 5.8开始,已经定义了一个宏框架,以允许静态数据安全存储在XS模块中,这些模块将从多线程Perl访问。

虽然这些宏主要是为使用多线程Perl而设计的,但它们的设计也是为了能够与非线程Perl一起使用。

因此,强烈建议所有使用静态数据的XS模块都使用这些宏。

获取要使用的宏模板集的最简单方法是指定-克(--全球)h2xs选项(请参见氢xs).

下面是一个使用宏的示例模块。

#定义PERL_NO_GET_CONTEXT#包括“EXTERN.h”#包括“perl.h”#包括“XSUB.h”/*全球数据*/#定义MY_CXT_KEY“盲鼠::_guts”XS_VERSIONtypedef结构{整数计数;字符名[3][100];}我的文本t;启动MY_CXT模块=盲鼠包装=盲鼠启动:{MY_CXT_INIT;MY_CXT.count=0;strcpy(MY_CXT.name[0],“无”);strcpy(MY_CXT.name[1],“无”);strcpy(MY_CXT.name[2],“无”);}整数newMouse(字符*名称)前言:dMY_CXT;代码:如果(MY_CXT.计数>=3){警告(“已经有3只盲鼠”);RETVAL=0;}其他{RETVAL=++MY_CXT.count;strcpy(MY_CXT.name[MY_CXT.count-1],name);}输出:RETVAL公司字符*get_mouse_name(索引)int索引前言:dMY_CXT;代码:if(索引>MY_CXT.count)呱呱(“只有三只瞎老鼠。”);其他的RETVAL=MY_CXT.name[索引-1];输出:RETVAL公司空隙克隆(…)代码:MY_CXT_CLONE公司;

MY_CXT参考

MY_CXT_键

此宏用于定义唯一键,以引用XS模块的静态数据。h2xs使用的建议命名方案是使用由模块名称、字符串“::_guts”和模块版本号组成的字符串。

#定义MY_CXT_KEY“MyModule::_guts”XS_VERSION
类型定义my_cxt_t

此结构typedef必须总是被呼叫我的文本.另一个CXT公司*宏假定存在我的文本typedef名称。

声明名为我的文本这是一个包含所有需要解释为本地的数据的结构。

typedef结构{int some_value;}我的文本t;
启动MY_CXT

始终将START_MY_CXT宏直接放在我的文本.

MY_CXT_初始化

MY_CXT_INIT宏初始化我的文本结构。

必须只能调用一次,通常在BOOT:部分中调用。如果要维护多个解释器,则应该在每个解释器实例中调用一次,但从现有解释器克隆的解释器除外。(但请参见“MY_CXT_CLONE”(见下文)

日期MY_CXT

在访问MY_CXT的所有函数中使用dMY_CXT宏(声明)。

MY_CXT车型

使用MY_CXT宏访问我的文本结构。例如,如果我的文本

typedef结构{int索引;}我的文本t;

然后使用此访问指数成员

dMY_CXT;MY_CXT.index=2;
aMY_CXT/pMY_CXT

日期MY_CXT计算可能非常昂贵,为了避免在每个函数中调用它的开销,可以使用一个MY_CXT/pMY_CXT公司宏,例如

无效sub1(){dMY_CXT;MY_CXT.index=1;sub2(aMY_CXT);}无效sub2(pMY_CXT){MY_CXT.index=2;}

类似于pTHX公司,当宏是多个参数中的第一个或最后一个时,有等效的形式,其中下划线表示逗号,即。_aMY_文本,一个MY_CXT_,_pMY_CXT公司pMY_CXT公司_.

MY_CXT_单独

默认情况下,当创建一个新的解释器作为现有解释器的副本时(例如,通过线程->创建()),两个解释器共享相同的物理mycxtt结构。打电话MY_CXT_单独(通常通过软件包克隆()函数),将获取该结构的逐字节副本,而任何未来的dMY_CXT都将访问该副本。

MY_CXT_INIT_INTERP(我的错误)
dMY_CXT_INTERP(我的错误)

这些是以显式解释器作为参数的宏版本。

请注意,这些宏只能在相同的源文件;也就是说,一个源文件中的dMY_CTX将访问不同于另一源文件中dMY_CTX的结构。

线程软件系统接口

从Perl 5.8开始,在C/C++级别,Perl知道如何将具有线程版本(例如getpwent_r())的系统/库接口包装到前端宏(例如getpwent())中,以正确处理与Perl解释器的多线程交互。这将以透明的方式发生,您只需要实例化一个Perl解释器。

这种包装总是在编译Perl核心源代码(定义了Perl_core)或Perl核心扩展(定义了TERL_EXT)时发生。在Perl核心之外编译XS代码时,在Perl 5.28之前不会进行包装。从该版本开始,您可以

#定义PERL_REENTRANT

在代码中启用包装。如果您正在使用这样的功能,如混合_第页-表单(就像Perl为多线程操作编译的那样)和_第页-less表单既没有定义好(结果不一致、数据损坏甚至崩溃的可能性更大),也不具有很强的可移植性。不幸的是,并非所有系统都具有_第页表单,但使用此#定义为您提供了Perl知道的每个系统上可用的任何保护。

示例

文件RPC.xs公司:某些ONC+RPC绑定库函数的接口。

#定义PERL_NO_GET_CONTEXT#包括“EXTERN.h”#包括“perl.h”#包括“XSUB.h”#包括<rpc/rpc.h>typedef结构netconfig netconfig;模块=RPC包=RPCSV公司*rpcb_gettime(主机=“localhost”)char*主机前言:time_t timep;代码:ST(0)=sv_newmotal();if(rpcbgettime(主机,&timep))sv_setnv(ST(0),(双)timep);Netconfig(网络配置)*getnetconfigent(netid=“udp”)字符*netid模块=RPC包=NetconfigPtr前缀=rpcb_空隙rpcb_DESTROY(网络连接)Netconfig*netconf代码:printf(“NetconfigPtr::DESTROY\n”);免费(netconf);

文件类型图:RPC.xs的自定义类型映射。(参见。perlx类型映射)

TYPEMAP(类型图)网络配置*T_PTROBJ

文件RPC.下午:RPC扩展的Perl模块。

包装RPC;要求出口商;需要DynaLoader;@ISA=qw(Exporter DynaLoader);@导出=qw(rpcb_gettime getnetconfigent);引导RPC;1;

文件快速测试.pl:RPC扩展的Perl测试程序。

使用RPC;$netconf=getnetconfigent();$a=rpcb_gettime();打印“time=$a\n”;打印“netconf=$netconf\n”;$netconf=getnetconfigent(“tcp”);$a=rpcb_gettime(“杨树”);打印“time=$a\n”;打印“netconf=$netconf\n”;

注意事项

XS代码可以完全访问包括C库函数在内的系统调用。因此,它能够干扰Perl核心或其他模块设置的内容,例如信号处理程序或文件处理程序。它可能会扰乱记忆或任何有害的东西。不要。

有些模块有一个事件循环,等待用户输入。两个这样的模块不太可能在单个Perl应用程序中充分协同工作。

一般来说,就perl程序而言,perl解释器将自己视为宇宙的中心。XS代码被视为一个帮助伙伴,可以完成perl无法完成的事情,或者做得不够快,但始终服从于perl。XS代码越接近此模型,发生冲突的可能性就越小。

存在冲突的一个领域是C语言环境。(请参见珍珠岩.)perl除了一个例外,除非另有说明,否则会将程序运行的底层语言环境设置为从环境传递到它的语言环境。这是与通用C语言程序的一个重要区别,其中,除非程序更改,否则底层语言环境是“C”语言环境。从v5.20开始,这个底层语言环境在词法范围之外对纯Perl代码完全隐藏使用区域设置除了POSIX模块中的几个函数调用之外,这些函数调用都必须使用它。但是底层的语言环境(其中有一个例外)被暴露给XS代码,影响了所有行为依赖于语言环境的C库例程。您的XS代码最好不要假设底层语言环境是“C”。例外情况是LC_名称locale类别,这是一个例外的原因是经验表明,对于XS代码来说,这可能是有问题的,而我们还没有关于其他区域设置类别这一类别之所以有问题,是因为用作小数点的字符可能会有所不同。许多欧洲语言使用逗号,而英语和Perl则需要一个点(U+002E:FULL STOP)。许多模块只能处理作为点的基数字符,所以perl试图这样做LC_数字启动到时“C”语言环境。任何setlocale()否则会改变它;这导致了一些失败。因此,从v5.22开始,perl尝试保持LC_名称始终设置为“C”用于XS代码。

总之,以下是预期的内容以及如何在XS代码中处理区域设置:

非缩放感知XS代码

请记住,即使您认为您的代码不支持本地语言,它也可能会调用一个库函数。希望此类函数的手册页会指出这种依赖关系,但文档并不完善。

当前区域设置公开给XS代码,除非可能LC_名称(在下一段中解释)。没有其他类别的问题报告。Perl在启动时进行初始化,以便当前的语言环境是由当时有效的用户环境指示的语言环境。请参见perllocale中的“ENVIRONMENT”.

然而,在v5.20之前,Perl在启动时初始化了一些东西,以便LC_数字已设置为“C”区域设置。但如果任何地方的代码更改了它,它将保持更改状态。这意味着你的模块不能依靠LC_名称特别是浮点数(包括版本字符串)中不能有点。如果不允许使用非点,则如果任何地方的任何人更改了语言环境,您的代码可能会中断。因此,v5.22更改了行为,以便Perl尝试保持LC_名称在“C”语言环境中,但在内部操作周围应该是其他内容。行为不端的XS代码始终能够更改语言环境,但最常见的情况是检查和处理这种情况。

支持本地的XS代码

如果需要用户环境中的语言环境,则不需要XS代码来设置语言环境,除非LC_名称,因为perl已经设置了其他组件。XS代码应该避免更改区域设置,因为它可能会对其他无关的代码产生负面影响,并且可能不是线程安全的。为了最小化问题,宏perlapi中的“STORE_LC_NUMERIC_SET_TO_NEEDD”,perlapi中的“STORE_LC_NUMERIC_FORCE_TO_UNDERLYING”、和perlapi中的“RESTORE_LC_NUMERIC”应该用于影响任何需要的更改。

但是,从Perl v5.28开始,语言环境在支持此功能的平台上是线程安全的。Windows从Visual Studio 2005开始提供此功能。许多其他现代平台支持线程安全的POSIX 2008功能。C类#定义 USE_THREAD_SAFE_LOCALE(使用_线程_安全_本地)如果此构建使用这些,则将定义。在Perl-space中,只读变量${安全_位置}如果生成未线程化,或者如果USE_THREAD_SAFE_LOCALE(使用_线程_安全_本地)定义;否则为0。

这种方法在主机下工作的方式是,每个线程都可以选择使用特定于它的区域设置(这是Windows和POSIX 2008功能),或者使用所有线程都可以访问的全局区域设置(此功能一直存在)。Windows和POSIX的实现方式完全不同。在Windows上,可以设置运行时,以便setlocale(3)函数要么只知道全局语言环境,要么只知道该线程的语言环境。在POSIX上,设置区域设置始终处理全局语言环境,并且创建了其他函数来处理每线程语言环境。Perl使其对Perl-space代码透明。它继续使用POSIX::setlocale(),解释器将其转换为每线程函数。

所有其他locale-sensive函数都会自动使用per-thread语言环境(如果已启用),如果未启用,则使用全局语言环境。因此呼吁设置语言环境如果当前线程使用的是每线程语言环境,则在POSIX系统上对该线程无效。如果perl是为单线程操作编译的,那么它不使用per-thread函数,因此设置语言环境按预期工作。

如果您已加载POSIX公司模块,您可以使用中给出的方法珍珠贝打电话给POSIX::setlocale要安全地更改或查询区域设置(在安全的系统上),或者可以使用新的5.28函数perlapi中的“Perl_setlocale”相反,这是系统的替代品setlocale(3),并透明地处理单线程和多线程应用程序。

有些与locale相关的库调用仍然不是线程安全的,因为它们在全局缓冲区中向所有线程返回数据。在过去,这些都无关紧要,因为语言环境根本就不是线程安全的。但现在您必须注意它们,以防在多线程应用程序中调用模块。已知的是

asctime()ctime()gcvt()[仅限POSIX.1-2001(POSIX.1~2008中删除的函数)]获取日期()wcrtomb(),如果其最终参数为NULLwcsrtombs(),如果其最终参数为NULLwcstombs()wctomb()

其中一些不应该在Perl应用程序中调用,对于其他应用程序,已经实现了线程安全版本:

asctime_()ctime_()Perl_langinfo()

这个_对从Perl 5.28开始,如果您使用

#定义PERL_REENTRANT

另请参见perlapi中的“Perl_langinfo”。您可以使用中给出的方法珍珠贝,以获得最佳的本地安全版本

POSIX::localeconv()POSIX::wcstombs()POSIX::wctomb()

请注意Localecov公司可通过以下方式获得perlapi中的“Perl_langinfo”.

其他的不应该在线程应用程序中使用。

一些模块可能会调用本地感知的非erl库。只要它不尝试使用系统查询或更改语言环境,这是可以的设置区域设置。但如果这些确实调用了系统设置语言环境,这些电话可能无效。相反,Perl_setlocale(_S)在任何情况下都有效。普通setlocale在多线程POSIX 2008系统上无效。它只在全局语言环境中操作,而每个线程都有自己的语言环境,不关注全局语言环境。由于将这些非Perl库转换为Perl_setlocale(_S)毫无疑问,v5.28中有一个新功能开关_全局_本地它将切换从中调用它的线程,以便任何系统设置语言环境调用将产生预期的效果。功能sync_locale(同步_定位)在返回到perl之前必须调用。

这个线程可以随意更改语言环境,它不会影响任何其他线程,除了那些已经切换到全局语言环境的线程。这意味着一个多线程应用程序可以使用一个外来库而没有问题地拥有一个线程;但最多只能占用一个线程。可能会出现不良结果。

在没有多线程区域设置支持的perls中,一些外部库,例如Gtk公司更改区域设置。这可能会导致Perl内核和其他模块出现问题。对于这些,在将控制返回给perl之前,从v5.20.1开始,调用函数sync_locale()XS应该足以避免大多数这些问题。在此之前,您需要一个纯Perl语句来完成此操作:

POSIX::setlocale(LC_ALL,POSIX::setlocal(LC_ALL));

或使用中给出的方法珍珠贝.

XS版本

本文档涵盖支持的功能外部实用程序::ParseXS(也称为xsubpp公司) 3.13_01.

作者

最初由Dean Roehrich撰写<roehrich@cray.com>.

自1996年以来由Perl Porters维护<perlbug@perl.org>.