ActiveState Home Page       Win32 FAQ


名前

perlwin32faq10 - エンベッドとエクステンド



概要

Perl for Win32のエンベッド及びエクステンドの技法



Perl for Win32のエクステンションを書くにはどうすればよいのでしょうか?

CやC++でPerl for Win32用エクステンションを書くのは、基本的には標準のPerl用エクステンションを書くのと同じです。 包括的な情報やその他の情報源については、perlxstutドキュメントに記載されています。

一つのPerlへの努力の結果、一々ソースディストリビューションからPerl for Win32をビルドしたり、エクステンションを書いたり、コンパイルしたりする必要はなくなりました。 Perlのインストーラが自動的にCコンパイラ(現在はVisual C++のみ)を検出し、エクステンションをコンパイルする際はそれを使用するようにPerlの設定をします。

但し、CやC++でエクステンションを書くのは簡単なことではありません。 エクステンションを書くことを考える前に、Windowsプラットファームでプログラムを書く経験を積む必要があります。



C/C++プログラムにPerlインタープリタをエンベッドするにはどうすればよいのでしょうか?

ビルド500では、これは実に簡単です。 Perl for Win32は、PERL_OBJECTというC++オブジェクトを通じてPerlの内部を公開します。 また、PERL_OBJECTを使うとシングルプロセス内で複数のPerlインタプリタを生成する作業が簡単になります。 自分自身のアプリケーションにPerl for Win32をエンベッドするには、次のものが必要になります。:

Perl for Win32をエンベッドするアプリケーションをコンパイルする前に、Perlのソースをコンパイルしなければなりません。 これは、Perlをソースからインストールし直すという意味ではありません。 Perlのソースをコンパイルするのは、コンパイルプロセスでDynaLoader.cというソースコードファイルが生成されるからなのです。 このソースコードは、アプリケーションで何らかのエクステンションを使用する場合に必要となるものです。

Perlのソースをコンパイルするには:

  1. Perlソースディストリビューションのwin32ディレクトリに移動し、必要に応じてMakefileを編集します。

  2. nmakeと入力します。これでPerlが全てコンパイルされます。

  3. nmake testと入力します。 これは、Perlが正しくビルドされており、全てのテストに合格することを確認するものです。

  4. さて、これでPerlがコンパイルされ、DynaLoader.cファイルが生成されました。 Perlのバイナリリリースをまだインストールしていなかった場合、make installコマンドを発行するとPerlをインストールすることができます。 Perlのバイナリリリースがインストール済みの場合は、このステップは必要ありません。

Perlソースのコンパイルが終わると、Perlのエンベッドバージョンのビルドに使用するソースツリーができています。 下記に、エンベッド用Perlインタプリタを含んだ簡単なプログラムを示します。 これは、perl.exeによく似ています。 Perlで使用されているコマンドラインスイッチは全て使用できますし、実行するスクリプト名を指定することもできます。 スイッチが全くない場合は、標準入力からPerlプログラムを読み込もうとし、end-of-fileが入力されるとプログラムを実行します。 このファイルを自機にMyPerl.cppとして保存します(ソースコードをアウトデント(字上げ)するのを忘れないように)。:


    /*

        1) perlhost.hをインクルードします。このヘッダはPERL_OBJECTで
           使用されるすべてのOSインターフェースを定義しています。
        2) ホストデータ構造体'CPerlHost'を定義します。
        3) perl_allocとperl_constructをコールする、CPerlHostメンバ関数
           PerlCreateをコールします。
        4) CPerlHostメンバ関数PerlParseをコールします。
        5) CPerlHostメンバ関数PerlRunをコールします。
        6) 終了すると、CPerlHostメンバ関数PerlDestroyをコールします。
           この関数はperl_destructとperl_freeをコールします。

        インクルードするXSUB.hは、PERL_OBJECTと連携するために
        perl_*関数を再定義するマクロを持ったファイルを取り込みます。
        これらのマクロは、名前の重複があった際は問題を引き起こすので
        注意してください。Perlの関数と同じ名前のローカル変数があると
        よく問題となります。
        名前の重複を追跡調査するには、VC++で'/P'オプションを使って
        コンパイルします。するとプリプロセッサが何をしたのか見ることが
        できるようになります。

    */
    #include <EXTERN.h>
    #include <perl.h>
    #define NO_XSLOCKS

    // PERL_OBJECTと連動するperl_*関数を再定義するマクロを取り込む
    //
    //
    #include <XSUB.h>

    /* Win32用 */
    #include "win32iop.h"
    #include <fcntl.h>
    #include <perlhost.h>
    #include <stdio.h>

    // DynaLoader用
    //
    char *staticlinkmodules[] = {
        "DynaLoader",
        NULL,
    };

    // これもDynaLoader用
    //
    EXTERN_C void boot_DynaLoader _((CV* cv _CPERLarg));

    static void
    xs_init(CPERLarg)
    {
        char *file = __FILE__;
        dXSUB_SYS;
        newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
    }

    CPerlObj *pPerl;  // ホストに使用されるPerlオブジェクト

    int main (int argc, char **argv, char **env)
    {
        CPerlHost host;     // Perlホスト
        host.PerlCreate();  // Perlインタープリタのインスタンスを生成

        printf("Welcome to my very own Perl!\n\n");

        // コマンドラインから引数を読み込む。コマンドラインでスクリプト名が
        // 指定されておらず、-eにも何も指定されていない場合、インタプリタは
        // (まるでPerlのように!)標準入力からコマンドを読み込もうとします。
        //
        host.PerlParse(xs_init, argc, argv, NULL);

        host.PerlRun();      // インタプリタを実行
        host.PerlDestroy();  // インタプリタを破壊
        return 0;
    }

このプラグラムをコンパイルするには、次のMakefileを使わなければなりません。 但し、LIBDIRは、インストールしてあるPerlバイナリのCOREサブディレクトリを指すように変更します。また、SRCDIRも、コンパイルしたPerlソースコードのsrcディレクトリを指すように変更します。 このファイルをMakefileとして保存します(各行をアウトデントするのを忘れないように)。:


    LIBDIR = C:\Perl\5.00500\lib\MSWin32-x86\CORE
    SRCDIR = C:\5.00500\src
    LIBS = oldnames.lib kernel32.lib user32.lib winspool.lib advapi32.lib \
           shell32.lib ole32.lib  oleaut32.lib netapi32.lib uuid.lib \
           wsock32.lib version.lib PerlCRT.lib $(LIBDIR)\PerlCore.lib

    CCFLAGS = -c -I$(LIBDIR) -Gf -W3 -DWIN32 -D_CONSOLE -DPERL_CORE \
              -O2 -MD -DNDEBUG -TP -GX -DPERL_OBJECT -UPERLDLL
    CCDYNA = $(CCFLAGS) -I$(SRCDIR)\ext\DynaLoader
    all:
        cl.exe $(CCFLAGS) -Fo.\MyPerl.obj MyPerl.cpp
        cl.exe $(CCFLAGS) -Fo.\win32.obj $(SRCDIR)\win32\win32.c
        cl.exe $(CCFLAGS) -Fo.\win32sck.obj $(SRCDIR)\win32\win32sck.c
        cl.exe $(CCFLAGS) -Fo.\DynaLoader.obj \
                          $(SRCDIR)\ext\DynaLoader\DynaLoader.c
        link.exe -nologo -nodefaultlib -release -machine:x86 $(LIBS) \
            -subsystem:console MyPerl.obj win32.obj win32sck.obj \
            DynaLoader.obj setargv.obj

PerlCRT.libが適切にインストールされているか、確認してください。 詳しくは、Perl for Win32のエクステンションを書くにはどうすればよいのでしょうか? を参照してください。

後はコマンドラインでnmake と入力するだけでMyPerl.cppをコンパイルできるようになりました。 (MyPerl.cppMakefileがカレントワーキングディレクトリに揃っていることを確認しておきます)。 これでMyPerl.exeが生成されます。このMyPerl.exeは、Perlのようにコマンドラインから実行することができます。:


    C:\MyPerl>myperl -e "print qq[Hello, World\n];"
    Welcome to my very own Perl!

    Hello, World

あるいは:


    C:\MyPerl>myperl
    Welcome to my very own Perl!

    print "Hello, world\n";
    ^Z
    Hello, world

このPerlもどきでモジュールやエクステンションを使う場合、2つ程対応方法があります。 簡単な方の方法は、その実行ファイルをperl.exeと同じ所にコピーすることです。

この方法で恒久的に利用する場合は、自分のプログラムにどこを検索するか教え込んでおく必要があります。 これは、以下のようにPERL5LIB環境変数をセットすることで実現できます。:


    set PERL5LIB=C:\Perl\5.00500\lib;C:\Perl\site\5.00500\lib;C:\Perl\site\lib

これでPerlもどきからモジュールやエクステンションを使用できるようになりました。:


    C:\MyPerl>MyPerl -MWin32::OLE -e "print $Win32::OLE::VERSION;"
    Welcome to my very own Perl!
    0.0807

次のサンプルは、Doug MacEachern と Jon Orwant による perlembedドキュメントのPerlPowerサンプルに基づいています。(perlembedドキュメントはPerlの一部であり、Perl for Win32にも付属しています)。 以下のプログラムをPerlPower.cppとして保存します。:


    /*
        定義済みのPERL_OBJECTと一緒にPerlをエンベッドする方法を示すのに
        perlembed.podのPerlPowerサンプルで必要となる変更点を示します。

        1) perlhost.hをインクルードします。このヘッダはPERL_OBJECTで
           使用されるすべてのOSインターフェースを定義しています。
        2) ホストデータ構造体'CPerlHost'を定義します。
        3) perl_allocとperl_constructをコールする、CPerlHostメンバ関数
           PerlCreateをコールします。
        4) CPerlHostメンバ関数PerlParseをコールします。
        5) CPerlHostメンバ関数PerlRunをコールします。
        6) perl_call_*関数を全部コールします。
        7) 終了すると、CPerlHostメンバ関数PerlDestroyをコールします。
           この関数はperl_destructとperl_freeをコールします。

        インクルードするXSUB.hは、PERL_OBJECTと連携するために
        perl_*関数を再定義するマクロを持ったファイルを取り込みます。
        これらのマクロは、名前の重複があった際は問題を引き起こすので
        注意してください。Perlの関数と同じ名前のローカル変数があると
        よく問題となります。
        名前の重複を追跡調査するには、VC++で'/P'オプションを使って
        コンパイルします。するとプリプロセッサが何をしたのか見ることが
        できるようになります。

    */

    #include <EXTERN.h>
    #include <perl.h>
    #define NO_XSLOCKS

    // PERL_OBJECTと連動するperl_*関数を再定義するマクロを取り込む
    //
    //
    #include <XSUB.h>

    /* Win32用 */
    #include "win32iop.h"
    #include <fcntl.h>
    #include <perlhost.h>

    // DynaLoader用
    //
    char *staticlinkmodules[] = {
        "DynaLoader",
        NULL,
    };

    // これもDynaLoader用
    //
    EXTERN_C void boot_DynaLoader _((CV* cv _CPERLarg));
    static void
    xs_init(CPERLarg)
    {
        char *file = __FILE__;
        dXSUB_SYS;
        newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
    }

    CPerlObj *pPerl;

    static void
    PerlPower(int a, int b)
    {
        dSP;                            /* スタックポインタを初期化      */
        ENTER;                          /* これ以降生成されるあらゆるもの */
        SAVETMPS;                       /* ...は、テンポラリ変数です。   */
        PUSHMARK(SP);                   /* スタックポインタを記憶        */
        XPUSHs(sv_2mortal(newSViv(a))); /* スタックにベースをプッシュ    */
        XPUSHs(sv_2mortal(newSViv(b))); /* スタックにexponetをプッシュ   */
        PUTBACK;                        /* ローカルスタックポインタをグローバル化 */
        perl_call_pv("expo", G_SCALAR); /* 関数をコール                  */
        SPAGAIN;                        /* スタックポインタをリフレッシュ */
                                        /* スタックから戻り値をポップ     */
        printf ("%d to the %dth power is %d.\n", a, b, POPi);
        PUTBACK;
        FREETMPS;                       /* 戻り値とXPUSHした"mortal"引数を */
        LEAVE;                          /* ...解放                        */
    }

    int main (int argc, char **argv, char **env)
    {
        CPerlHost host;

        host.PerlCreate();

        // エンベッドにargvやargcは使いません。代わりに、
        // 引数"-e 0"が与えられたものとします。
        //
        char *embedding[] = { "", "-e", "0" };
        host.PerlParse(xs_init, 3, embedding, NULL);

        // expo()関数を定義
        //

        perl_eval_pv("sub expo { my($a, $b) = @_; return $a ** $b; }", TRUE);

        host.PerlRun();

        PerlPower(3, 4);                      /*** 3 ** 4 を計算 ***/

        host.PerlDestroy();
        return 0;
    }

PerlPower.cppのコンパイルには、次のMakefileを使います。:


    LIBDIR = C:\Perl\5.00468\lib\MSWin32-x86\CORE
    SRCDIR = C:\betab9\src

    LIBS = oldnames.lib kernel32.lib user32.lib winspool.lib advapi32.lib \
           shell32.lib ole32.lib  oleaut32.lib netapi32.lib uuid.lib \
           wsock32.lib version.lib PerlCRT.lib $(LIBDIR)\PerlCore.lib

    CCFLAGS = -c -I$(LIBDIR) -Gf -W3 -DWIN32 -D_CONSOLE -DPERL_CORE \
              -O2 -MD -DNDEBUG -TP -GX -DPERL_OBJECT -UPERLDLL
    CCDYNA = $(CCFLAGS) -I$(SRCDIR)\ext\DynaLoader

    all:
        cl.exe $(CCFLAGS) -Fo.\PerlPower.obj PerlPower.cpp
        cl.exe $(CCFLAGS) -Fo.\win32.obj $(SRCDIR)\win32\win32.c
        cl.exe $(CCFLAGS) -Fo.\win32sck.obj $(SRCDIR)\win32\win32sck.c
        cl.exe $(CCFLAGS) -Fo.\DynaLoader.obj \
                          $(SRCDIR)\ext\DynaLoader\DynaLoader.c
        link.exe -nologo -nodefaultlib -release -machine:x86 $(LIBS) \
            -subsystem:console PerlPower.obj win32.obj win32sck.obj \
            DynaLoader.obj setargv.obj

MyPerl.cpp用のMakefileのサンプルと同様に、SRCDIRLIBDIRが適切なディレクトリを示すように修正します。 PerlPower.cppをコンパイルするには、コマンドラインでnmakeと入力します(PerlPower.cppMakefileがカレントワーキングディレクトリに揃っていることを確認しておきます)。 これでPerlPower.exeが生成されます。 PerlPower.exeを実行するには、コマンドラインで以下のようにします。:


    C:\PerlPower>PerlPower
    3 to the 4th power is 81.

Perlのエンベッドにまつわる問題の概要を知るには、とにかくperlembedドキュメントをくまなく読むことです。それから、Perl for Win32のエンベッドのガイドとしてMyPerl.cppPerlPower.cppを使ってみるとよいでしょう。



標準ディストリビューションのperlをエンベッドしたプログラムがあるのですが、Perl for Win32だとコンパイルしようとしません。どうしてでしょうか?

Perl for Win32のperlインタプリタは、標準のPerlのものとは若干異なったふるまいをします。 Perl for Win32でエンベッドする方法は、 C/C++プログラムにPerlインタープリタをエンベッドするにはどうすればよいのでしょうか? を参照してください。



著者および著作権について

この FAQ は、元々Evangelo Prodromou evangelo@endcontsw.comが作成し、保守していたものです。 現在、この文書は、O'Reilly社のBrian Jepson、ActiveState社のDavid GroveとDavid Dmytryshynが改訂・更新しています。

このFAQはパブリックドメインですが、使用する場合はオリジナルの作者について明示してください。

ActiveState Home Page       Win32 FAQ