病态污染的Perl

病态污染的Perl

目录

Inline in Action - C中的简单示例
Hello, world
另一位____黑客
关于XS和SWIG怎么办?
单行脚本
C支持的平台
Inline语法
精美佳肴 - 一瞥C菜谱
外部库
接受所有类型
C之外的一些东西
见Perl运行。跑,Perl,跑!
Inline的未来
结论

没有一种编程语言是完美的。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附带文档中的perlgutsperlapi来了解更多关于简单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实用工具,如bashlessmakegcc和当然还有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数据类型。这些是:intlongdoublechar*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。但如果我们无论如何都需要它,编写一个支持floattypemap文件是微不足道的。

文件将如下所示

    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上打开一个问题或拉取请求来帮助我们。