病态污染的Perl
病态污染的Perl
目录 |
•Inline in Action - C中的简单示例 |
没有一种编程语言是完美的。Perl非常接近。P! e! r! l? :-( 还不是“完美”。有时使用另一种语言完成部分工作是有意义的。你可能有一个稳定的、现成的代码库可以利用。也许性能最大化是问题所在。也许你只是“知道如何这样做”。或者很可能,这是管理层强制你接受的项目要求。无论原因如何,大多数时候使用Perl,但需要在必要时调用其他东西,不是很好吗?
Inline.pm
是一个将其他编程语言粘接到Perl的新模块。它允许你直接在Perl脚本和模块中编写C、C++和Python代码。这与你在C程序中编写内联汇编语言的方式在概念上相似。因此得名:Inline.pm
。
Inline背后的基本哲学是这样的:“尽可能让Perl与其他编程语言易于使用,同时确保用户的体验保留Perl的DWIMity”。为了实现这一点,Inline必须消除接口定义语言、makefile、构建目录和编译等烦恼。你只需编写代码并运行它。就像Perl一样。
Inline将默默处理所有繁琐的实现细节并“做正确的事”。它分析你的代码,必要时编译它,创建正确的Perl绑定,加载一切,并运行整个程序。这种效果是你现在可以在另一种语言中编写函数、子程序、类和方法,并像使用Perl一样调用它们。
Inline in Action - C中的简单示例
Inline以一种完全革命性的方式解决了旧问题。仅仅描述Inline并不能真正公正地对待它。它应该被看到才能充分欣赏。这里有一些示例,让你对模块有所了解。
Hello, world
似乎任何程序员在学习一种新技术时都想用它来问候地球。遵循这一传统,这里是用Inline编写的“Hello, world”程序。
use Inline C => <<'END_C';
void greet() {
printf("Hello, world\n");
}
END_C
greet;
只需从命令行运行此脚本,它就会打印(你猜对了)
Hello, world
在这个例子中,Inline.pm
使用编程语言名称“C”和一个包含该语言源代码片段的字符串实例化。这段C代码定义了一个名为greet()
的函数,它被绑定到Perl子程序&main::greet
。因此,当我们调用greet()
子程序时,程序将在屏幕上打印我们的信息。
你可能想知道为什么没有#include
语句来包含像stdio.h
这样的文件?这是因为Inline::C会自动在代码顶部添加以下行
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "INLINE.h"
这些头文件包含了所有标准系统头文件,所以除非你处理的是非标准库,否则几乎不需要使用#include
。这与Inline的简化哲学相符。(我在哪里听过这句话?)
又一个____黑客
下一个合乎逻辑的问题是,“我该如何在Perl和C之间传递数据?”在这个例子中,我们将向C函数传递一个字符串,并让它返回一个新的Perl标量。
use Inline C;
print JAxH('Perl');
__END__
__C__
SV* JAxH(char* x) {
return newSVpvf("Just Another %s Hacker\n", x);
}
当你运行这个程序时,它会打印
Just Another Perl Hacker
你可能已经注意到这个例子与上一个例子编写的不同。使用use Inline
语句指定了使用的语言,但没有指定源代码。这是Inline寻找源代码的指示,即在程序末尾的特殊标记__C__
之后。
这里展示的概念是我们可以在C函数中传入和传出Perl数据。使用默认的Perl类型转换,Inline可以轻松地将所有基本Perl数据类型转换为C,反之亦然。
这个例子使用了几个Inline的高级概念。其返回值类型为SV*
(或标量值)。标量值是Perl最常用的内部类型。此外,还调用了Perl内部函数newSVpfv()
,使用熟悉的sprintf()
语法从一个字符串创建一个新的标量值。你可以通过阅读Perl附带文档中的perlguts
和perlapi
来了解更多关于简单Perl内部的信息。
那么XS和SWIG呢?
让我们暂时偏离一下,思考一下“为什么使用Inline?”
已经有两个主要的设施可以用来扩展Perl,即XS和SWIG。就Perl而言,它们的功能相似。而且,与Inline相比,它们都相当难以学习。由于SWIG在实践中并没有像XS那样被广泛使用,所以我只讨论XS。
设置和使用XS环境涉及一个很大的学习曲线。你需要非常熟悉以下文档
* perlxs
* perlxstut
* perlapi
* perlguts
* perlcall
* perlmod
* h2xs
* xsubpp
* ExtUtils::MakeMaker
使用Inline,你可以在几分钟内启动并运行。有一个C菜谱,包含很多短小但完整的程序,你可以将其扩展到现实生活中的问题。不需要了解后台复杂的构建过程。你甚至不需要自己编译代码。Perl程序员不会浪费时间去做像编译这样的愚蠢事情。“修改、运行、修改、运行”是我们的生活方式。Inline处理了所有细节,除了编写C代码。
Inline的另一个优点是,你可以在脚本中直接使用它。正如我们很快就会看到的,你甚至可以在Perl单行脚本中使用它。与XS和SWIG相比,你总是设置一个完全独立的模块,即使你只有一两个函数。Inline使简单的事情变得简单,使困难的事情成为可能。就像Perl一样。
最后,Inline支持多种编程语言(不仅仅是C和C++)。截至本文写作时,Inline支持C、C++、Python和CPR。计划添加更多。
单行脚本
Perl以其单行脚本而闻名。Perl单行脚本是一小段Perl代码,可以完成其他语言需要更长时间才能完成的任务。这是Perl黑客用来展示其编程肌肉的流行技术之一。
那么你可能想知道:“Inline是否足够强大,可以产生既是单行脚本也是有效的C扩展的脚本?”当然可以!看这里
perl -e 'use Inline C=>
q{void J(){printf("Just Another Perl Hacker\n");}};J'
用XS试试看!我们甚至可以将之前讨论过的更复杂的Inline JAxH()
写成单行脚本
perl -le 'use Inline C=>
q{SV*JAxH(char*x){return newSVpvf("Just Another %s Hacker",x);}};print JAxH+Perl'
我过去几个月一直用这句话作为我的电子邮件签名。直到Bernhard Muenzer在comp.lang.perl.modules
上贴出这个宝贝,我才发现它很酷。
#!/usr/bin/perl -- -* Nie wieder Nachtschicht! *- -- lrep\nib\rsu\!#
use Inline C=>'void C(){int m,u,e=0;float l,_,I;for(;1840-e;putchar((++e>907
&&942>e?61-m:u)["\n)moc.isc@rezneumb(rezneuM drahnreB"]))for(u=_=l=0;79-(m
=e%80)&&I*l+_*_<6&&26-++u;_=2*l*_+e/80*.09-1,l=I)I=l*l-_*_-2+m/27.;}';&C
C语言支持的平台
内联C可以在我已经测试过的所有Perl平台上工作,包括所有常见的Unix系统和最新的Microsoft Windows版本。唯一的限制是,你必须有与构建你的perl
二进制文件相同的编译器和make
实用工具。
Inline已经在Linux、Solaris、AIX、HPUX和所有最近的BSD系统上成功使用。
在MS Windows上使用Inline有两种常见的方法。第一种是与ActiveState的ActivePerl for MSWin32一起使用。为了在那个环境中使用Inline,你需要一份MS Visual C++ 6.0。它包含了cl.exe
编译器和nmake
构建实用工具。实际上,你只需要这些部分。视觉组件对Inline不是必需的。
另一种选择是使用Cygwin实用工具。这是一个实际的Windows上的Unix移植层。它包括了所有最常用的Unix实用工具,如bash
、less
、make
、gcc
和当然还有perl
。
Inline语法
Inline与您习惯的大多数Perl模块略有不同。它不会将任何函数导入您的命名空间,也没有任何面向对象的方法。它的整个接口是通过'use Inline ...'
命令指定的。一般地,Inline的使用方法是
use Inline C => source-code,
config_option => value,
config_option => value;
其中C
是编程语言,source-code
是一个字符串、文件名或关键字“DATA
”。您可以跟任意数量的可选的“keyword => value
”配置对。如果您使用的是“DATA”选项,并且没有配置参数,您可以简单地这样写
use Inline C;
美食盛宴 - C编程宝典一瞥
秉承O'Reilly的《Perl宝典》一书的精神,Inline提供了一个名为C-Cookbook的manpage。在其中,您将找到满足您的Inline需求所需的食谱。以下是一些简单易学的美味佳肴,您可以在短时间内轻松制作。祝您用餐愉快!
外部库
Inline最常见的实际需求可能是使用它从Perl访问现有的编译C代码。这很容易做到。关键是为每个您想在Perl空间中暴露的函数编写一个包装函数。包装器调用实际函数。它还处理参数的传递。以下是一个简短的Windows示例,它显示一个带有消息、标题和“OK”按钮的文本框
use Inline C => DATA =>
LIBS => '-luser32',
PREFIX => 'my_';
MessageBoxA('Inline Message Box', 'Just Another Perl Hacker');
__END__
__C__
#include <windows.h>
int my_MessageBoxA(char* Caption, char* Text) {
return MessageBoxA(0, Text, Caption, 0);
}
此程序调用MSWin32 user32.dll
库中的一个函数。包装器确定要从Perl传递的参数的类型和顺序。尽管真实的MessageBoxA()
需要四个参数,但我们可以用两个参数暴露给Perl,并改变它们的顺序。为了避免C中的命名空间冲突,包装器必须有不同的名称。但通过使用PREFIX
选项(与XS PREFIX
选项相同)我们可以在Perl中绑定到原始名称。
兼容所有类型
旧版本的Inline仅支持五种C数据类型。这些是:int
、long
、double
、char*
和SV*
。这已经足够了。所有基本的Perl标量类型都由这些表示。更复杂的事物,如引用,可以通过使用通用的SV*
(标量值)类型来处理,然后在C函数内部自己编写映射代码。
在Perl的SV*
和C类型之间进行转换的过程称为类型映射。在XS中,您通常通过使用typemap
文件来完成此操作。每个Perl安装中都有一个默认的typemap
文件,位于/usr/lib/perl5/5.6.0/ExtUtils/typemap
或类似位置。此文件包含20多种C类型的转换代码,包括所有Inline默认值。
从版本0.30开始,Inline不再具有任何内置类型。它所有的类型都仅从typemap
文件中获取。因为它使用Perl的默认typemap
文件作为其默认设置,所以实际上它有许多类型可以自动获取。
这种设置提供了很大的灵活性。您可以通过使用TYPEMAPS
配置选项来指定自己的typemap
文件。这不仅允许您用自己的转换代码覆盖默认设置,还意味着您可以向Inline添加新类型。通过这种方式扩展Inline语法的最大优点是,已经有很多针对各种API的typemaps可用。如果您过去做过自己的XS编码,您可以直接使用现有的typemap
文件。无需任何更改。
让我们看看编写自己的typemaps的一个小例子。出于某种原因,C类型float
在默认的Perl typemap
文件中未表示。我想是因为Perl的浮点数始终以类型double
存储,其精度高于float
。但如果我们无论如何都需要它,编写一个支持float
的typemap
文件是微不足道的。
文件将如下所示
float T_FLOAT
INPUT
T_FLOAT
$var = (float)SvNV($arg)
OUTPUT
T_FLOAT
sv_setnv($arg, (double)$var);
不深入细节,此文件提供了两段代码。一段用于将SV*
转换为浮点数,另一段用于相反的操作。现在我们可以编写以下脚本
use Inline C => DATA =>
TYPEMAPS => './typemap';
print '1.2 + 3.4 = ', fadd(1.2, 3.4), "\n";
__END__
__C__
float fadd(float x, float y) {
return x + y;
}
超越C的某个地方
Inline的主要目标是使使用Perl与其他编程语言变得容易。这不仅仅限于C。Inline的初始实现仅支持C,语言支持直接集成到Inline.pm
中。从那时起,事情发生了很大的变化。现在,Inline支持多种编译和解释性语言。它保持面向对象类型的结构实现,其中每种语言都有自己的独立模块,但它们可以从基础Inline模块继承行为。
在我加入ActiveState的第二天,一位年轻人走过来。``嗨,我叫尼尔·沃基斯。我刚刚修改了您的Inline模块以使其与C++兼容。”
尼尔很快就发现,他是一家本地大学的计算机科学学生。当时他正在ActiveState兼职工作,不知怎的发现了Inline。我兴奋极了!我一直想追求新的语言,但不知道如何找到时间。现在,我就在离我15英尺远的地方找到了答案!
在接下来的几个月里,尼尔和我利用业余时间将Inline转变为将新语言粘接到Perl上的通用环境。我将所有与C相关的代码从Inline中移除,并将其放入Inline::C。尼尔开始构建Inline::CPP和Inline::Python。我们一起提出了一种新的语法,它允许使用多种语言,并更容易进行配置。
以下是一个使用Inline Python的示例程序
use Inline Python;
my $language = shift;
print $language,
(match($language, 'Perl') ? ' rules' : ' sucks'),
"!\n";
__END__
__Python__
import sys
import re
def match(str, regex):
f = re.compile(regex);
if f.match(str): return 1
return 0
这个程序使用Python正则表达式来展示“Perl很棒!”
由于Python支持其自己的Perl标量、数组和哈希版本,因此Inline::Python可以轻松且合逻辑地在它们之间转换。如果您向Python传递一个哈希引用,它将将其转换为字典,反之亦然。尼尔甚至有为从Python代码调用Perl提供机制。有关更多信息,请参阅Inline::Python文档。
看Perl运行。运行Perl,跑!
Inline是编写Perl C扩展的绝佳方式。但有没有一种同样简单的方法可以将Perl解释器嵌入到C程序中?有一天,我自己思考这个问题。为C编写Inline功能不是我的菜。
将Perl嵌入C的正常方法涉及跳过许多圈套来引导一个perl解释器。对一行脚本来说太乱了。并且您需要编译C。这不太像Inline。但如果你能将你的C程序传递给一个可以将其传递给Inline的perl程序怎么办?然后你可以写这个程序
#!/usr/bin/cpr
int main(void) {
printf("Hello, world\n");
}
并且只需从命令行运行它。解释型C!
因此,一种新的编程语言诞生了。《CPR》(C Perl Run)。赋予它生命力的Perl模块被称为 Inline::CPR
。
当然,从严格意义上讲,CPR并不是一门真正的独立语言。但你可以这样理解。CPR就像C一样,不过你可以在任何时候调用Perl5 API,而不需要任何额外的代码。事实上,CPR通过自己的CPR包装器API重新定义了该API。
关于CPR,有几种不同的看法:它可以被视为“一门新语言”,是“将Perl嵌入C的简单方法”,或者仅仅是一个“有趣的技巧”。我更倾向于后者。CPR可能远远不能满足大多数人的嵌入需求。但与此同时,它也是一个非常容易尝试和可能重新定义Perl5内部API的方式。我收到的对CPR的最高赞誉来自于我的ActiveState同事Adam Turoff,他说:“我感觉我的脑袋就像被一块砖头裹住了一样。”我希望接下来的例子也能让你有同样的感觉。
#!/usr/bin/cpr
int main(void) {
CPR_eval("use Inline (C => q{
char* greet() {
return \"Hello world\";
}
})");
printf("%s, I'm running under Perl version %s\n",
CPR_eval("&greet"),
CPR_eval("use Config; $Config{version}"));
return 0;
}
运行这个程序会输出
Hello world, I'm running under Perl version 5.6.0
使用eval()
调用,这个CPR程序调用Perl并告诉它使用Inline C添加一个新函数,然后CPR程序随后调用该函数。我自己也觉得有点头痛。
Inline的未来
Inline 0.30版本是为了方便Perl社区的其他人为Perl贡献新的语言绑定而编写的。在那个发布日,我宣布了Inline邮件列表的诞生,[email protected]。 这将作为讨论所有Inline问题的主要论坛,包括新功能的提议和新ILSM的编写。
在2001年,我希望看到Java、Ruby、Fortran和Bash的绑定。我并不打算自己编写所有这些。但我可能会启动其中一些,看看是否有人有兴趣接手。如果你 有 想要参与Inline开发的愿望,请加入邮件列表([email protected])并发言。
目前,我主要的目标是使基础Inline模块尽可能简单、灵活和稳定。我还希望看到Inline::C能够成为XS的合适替代品;至少在大多数情况下。
结论
使用XS真是太困难了。至少当你将它与我们熟悉和喜爱的其他Perl比较时。Inline利用了现有的将Perl和C结合的框架,并将它们包装成一个易于消化的药丸。作为额外的好处,它还为将其他编程语言绑定到Perl提供了一个优秀的框架。你可以说,这是一项“Perl完美的”解决方案!
标签
反馈
这篇文章有什么问题吗?请通过在GitHub上打开一个问题或拉取请求来帮助我们。