Spica*

プログラミングの話。

PythonでCバインディング試してみた

以前MIDIハッカソンで、「PythonC/C++とお話できます!」的な話を聞いていて、ずっと気になっていたのでちょっと試すだけ試してみた。PythonからCプログラムを呼び出す | 象歩を参考に、そのまま動かすところまでやってみた。CもPythonも未経験だけど、可能性広げる意味で。。

コンパイルのところでちょっと詰まったのでメモっておく。

環境

  • Mac OS X El Capitan
  • HomebrewでインストールしたPython(v2.7.10)

ソースを用意する

先ほどの記事を参考に、 hello.chelloWrapper.c を用意する。「1.ソースコード」の節と「2.wraper コード」の節のソースコードをそれぞれそのままコピペでOK。

コンパイル

コンパイルに使用するコマンドは、参考記事では下記のようになってる。

gcc -fpic -o hello.o -c hello.c
gcc -fpic -I/usr/include/python -o helloWrapper.o -c helloWrapper.c
gcc -shared hello.o helloWrapper.o -o hellomodule.so

しかし、homebrewでインストールしたpythonでは /usr/include/python にヘッダファイルがあるわけではないので、そのままでは動かない。

$ gcc -fpic -I/usr/include/python -o helloWrapper.o -c helloWrapper.c
helloWrapper.c:2:10: fatal error: 'Python.h' file not found
#include "Python.h"
         ^
1 error generated.

$ ls /usr/include/python
ls: /usr/include/python: No such file or directory

この辺を解決しなきゃ。。

hello.cコンパイル

とりあえず一行目は動くので、動かして hello.o を作る。warning出るけど…

$ gcc -fpic -o hello.o -c hello.c
hello.c:9:2: warning: implicitly declaring library function 'printf' with type 'int (const char *, ...)'
        printf("こんにちは、私は %s  %s です。\n", adrs, name);
        ^
hello.c:9:2: note: include the header <stdio.h> or explicitly provide a declaration for 'printf'
1 warning generated.

$ ls
hello.c         hello.o         helloWrapper.c

helloWrapper.cコンパイル

続けて、 helloWrapper.cコンパイルPython.hをインクルードしているので、これがあるパスを見つけて、 -I の後に記述しなくちゃいけない。

で、ググってたら、python-config --prefixというコマンドがある のを見つけたので実行してみると、ビンゴっぽかった。

$ python-config --prefix
/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7

$ ls `python-config --prefix`
Headers@   Python*    Resources/ bin/       include/   lib/

Headers内に Python.h があったので helloWrapper.cコンパイルは下記のコマンドでいけた。

$ gcc -fpic -I`python-config --prefix`/Headers -o helloWrapper.o -c helloWrapper.c
helloWrapper.c:31:10: warning: incompatible pointer types initializing 'PyCFunction' (aka 'PyObject *(*)(PyObject *, PyObject *)') with an expression of type
      'PyObject *(PyObject *, PyObject *, PyObject *)' [-Wincompatible-pointer-types]
        {"out", hello_out, METH_VARARGS | METH_KEYWORDS},
                ^~~~~~~~~
1 warning generated.

$ ls
hello.c         hello.o         helloWrapper.c  helloWrapper.o

hellomodule.soの作成

最後、 hellomodule.so 共有モジュールの作成。これもさっきのパスにヒントがあって、 -L オプションには、libディレクトリを指定すれば良いようだった。

-l のオプションですごく迷ったのだけど、 compilation - C can't compile - symbol(s) not found for architecture x86_64 - Stack Overflow とか見ると、静的ライブラリlibimplementations.aの場合は-limplementationsという指定にしていた。

libディレクトリには、libpython2.7.dylibというファイル名があったので、-lpython2.7かなと思って下記のコマンドを発行。

$ gcc -L`python-config --prefix`/lib -lpython2.7 -shared hello.o helloWrapper.o -o hellomodule.so
$ ls
hello.c         hello.o         helloWrapper.c  helloWrapper.o  hellomodule.so*

いけた。ここまでで材料が揃った。

実行する

$ python
Python 2.7.10 (default, Jul 13 2015, 12:05:58)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
>>> hello.add(2, 3)
5
>>> hello.out("大原", "麗子")
こんにちは、私は 大原 の 麗子 です。
>>> quit()

おおー!

今度はnode.jsのネイティブバインディングやってみたいですね!