C言語アプリケーションにPyPyを埋め込む

先週金曜日のPyGrunn 2016カンファレンスで、Python cffiパッケージと、PyPyをC言語アプリケーションに埋め込む時にそれをどう活かすかについて話をしました。この講演のビデオは後日公開されます。

cffi 1.5.0のリリース、続いてそれがPyPy 5に組み込まれたことで、PyPyの埋め込みが可能になりました。Pythonコードをダイナミックライブラリにコンパイルすれば、他のどのような言語でも使えるのです。この記事ではその方法を説明します。

埋め込みAPI

最初のステップは、C言語アプリケーションからそのPythonコードを呼び出す方法を定める、インターフェース的なものを定義することです。C言語の関数プロトタイプを使ってこれを指定する必要があります。今回の例では、ある種の計算行う関数を紹介しますが、もちろん他のものでも可能です。

float compute(float first, float second);

次に、この計算をPythonで実装しなければなりません。

from my_library import ffi, lib

@ffi.def_extern()
def compute(first, second):
    """ Compute the absolute distance between two numbers. """
    return abs(first - second)

この実装のスニペットには、適切に埋め込むための特別な仕様が含まれています。1行目で、ダイナミックライブラリからffilibオブジェクトをインポートします。これによって、cffiが提供する関数へアクセスしての実装が可能になり、メモリの割り当てなど、より複雑なタスクに利用できるようになります。my_libraryという名前と、ダイナミックライブラリのどの名前に対応するかは下記に定義しました。

次にスニペットを見て気づくのは、@ffi.def_externデコレータです。これは、生成されたC言語のパブリックAPIに、デコレート関数が公開されることをcffiに伝えます。デコレートされた関数はAPIの定義で命令されたプロトタイプにマッピングされ、それらの引数と戻り値は自動的に変換されるのです。

ライブラリの生成スクリプト

以上で埋め込むAPIとその実装が用意できたので、実際にどこかに埋め込まなければなりません。そのために、ここではダイナミックライブラリを生成するスクリプトを使用します。埋め込みスクリプトを使うには、上に挙げた2つのスニペットをapi.hファイルとimplementation.pyファイルに記述する必要があります。

import cffi
ffi = cffi.FFI()

ffi.embedding_api(open("api.h").read())
ffi.embedding_init_code(open("implementation.py").read())

ffi.set_source("my_library", "")
ffi.compile(verbose=True)

埋め込みスクリプトは非常に単純です。外部APIを指定し、実装を展開しましょう。どちらもディスクから読み取り、前のセクションで命令した2つのスニペットに対応します。

ソースを指定した後は、cffiにライブラリの名前、今回の例ではmy_libraryを伝えなければなりません。この箇所では、さらに別のCのソースを追加し、例えば適切なヘッダファイルを包括するなどして、APIヘッダファイルの型を与えることができます(これはembedding_apiでは許可されていません)。残すは、ソースをコンパイルし、ライブラリファイルを作成するのみになりました。

スクリプトを走らせるといくつかの出力とライブラリが生成されます。

$ pypy embed.py
generating ./my_library.c
running build_ext
building 'my_library' extension
...

$ ls my_library.dylib
-rwxr-xr-x  1 djinn  staff  9856 May 15 14:46 my_library.dylib

ここまで来たら、後はどこかで使ってみるだけです。

ホストアプリケーション

埋め込みPythonコードは実際、とてもシンプルに使えます。次のコードを用いるだけです。

#include <stdio.h>
#include "api.h"

int main(void) {
    float result = compute(12.34f, 10.0f);
    printf("The result: %f\n", result);

    return 0;
}

見てのとおり、Pythonコードの呼び出しに要する作業はほとんどありません。CPython埋め込みのAPIを使う場合、インタプリタを立ち上げ、多くのパラメータ処理や戻り値の変換を行う必要があるでしょう。しかし、cffiは違います。生成したライブラリがそれら全ての処理を行うので、あなたは本来のもの作りに集中できます。

最後に、コンパイルと実行の方法をお見せしましょう。

$ clang -o test test.c my_library.dylib

$ ./test
The result: 2.340000

できました。ほんの数行のコードで、PyPyインタプリタを起動し、ネイティブのC言語関数であるかのようにPythonコードを実行するC言語のプログラムが書けてしまいました。もちろん、ここで紹介したのはほんの基礎ですが、その技術は実にパワフルです。関連の情報はcffiのドキュメンテーションを見てください。

この記事に興味を持たれたら、TwitterFacebookでフォローしてください。感謝します。