Boost.pythonを試してみようとhomebrewで導入してみたところ、少々ハマりましたのでメモっておきます。
環境
- OS X El Capitan(10.11.3)
- Python 2.7.11 (homebrew)
- boost 1.59 (homebrew)
- boost-python 1.59 (homebrew)
使用例(C++)
c++の自乗を返す関数をpythonから呼んでみます。
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
int square(int a)
{
return a*a;
}
BOOST_PYTHON_MODULE(hello_ext)
{
using namespace boost::python;
def("square",square);
}
hello_ext.soをビルドするためのMakefileです。
all: hello_ext.so
CC = g++
FRAMEWORK_PATH=/usr/local/opt/python/Frameworks
INCLUDE_PATH=$(FRAMEWORK_PATH)/Python.framework/Versions/2.7/include/python2.7
hello_ext.o: hello_ext.cpp
$(CC) -Wall -O2 -fPIC -c $< -I$(INCLUDE_PATH)
hello_ext.so: hello_ext.o
$(CC) -shared -o $@ $< -lboost_python -F$(FRAMEWORK_PATH) -framework Python
pythonの呼び出し側です。
#!/usr/bin/env python
import hello_ext
print hello_ext.square(2)
ビルドと実行結果です。
$ make
g++ -shared -o hello_ext.so -I/usr/local/opt/python/Frameworks/Python.framework/Versions/2.7/include/python2.7 hello_ext.o -lboost_python -F/usr/local/opt/python/Frameworks -framework Python
$ python hello.py
4
なるほど、昔ながらのPython拡張に比べてコードがすっきりしてよいですね。
使用例(Objective-C++)
既存のフレームワークを使いたいなどの理由で、Objective-Cを混在させるにはObjective-C++を使うといけますね。下記をhello_ext.mmとして作成します。
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#import <Foundation/Foundation.h>
int square(int a)
{
return a*a;
}
void nslog(void)
{
NSLog(@"Hello");
}
BOOST_PYTHON_MODULE(hello_ext)
{
using namespace boost::python;
def("square",square);
def("nslog",nslog);
}
Makefileは下記です。コンパイル時には-x objective-c++をつけ、リンク時にはフレームワークのパスとフレームワーク名をつけます。
all: hello_ext.so
CC =clang++
FRAMEWORK_PATH=/usr/local/opt/python/Frameworks
INCLUDE_PATH=$(FRAMEWORK_PATH)/Python.framework/Versions/2.7/include/python2.7
FRAMEWORK_PATH2=/System/Library/Frameworks
STD_CPP_PATH = /usr/include/c++/4.2.1/
FRAMEWORK_OPTIONS=-F$(FRAMEWORK_PATH) -framework Python -F$(FRAMEWORK_PATH2) -framework Foundation
hello_ext.o: hello_ext.mm
$(CC) -x objective-c++ -Wall -O2 -fPIC -c $< -I$(INCLUDE_PATH) -I$(STD_CPP_PATH)
hello_ext.so: hello_ext.o
$(CC) -shared -o $@ $< -lboost_python $(FRAMEWORK_OPTIONS)
.PHONY: clean
clean:
rm *.so *.o
下記で呼び出せます。
import hello_ext
hello_ext.nslog()
print hello_ext.square(2)
実行結果は下記です。
2017-04-09 11:45:29.818 Python[7710:333116] Hello
4
ハマった点と解決までの経緯
まず公式チュートリアルはbjamを使えと書いてあります。bjamはhomebrewの boost-buildを入れるとインストールできますが、Jamfileとやらのパスを指定せよといわれ、homebrewでのインストール先がわからず断念しました。
次にBoost.Python の機能をざっと紹介してみるなどの先人に習ってg++でビルドをしてみましたが、引数の受け渡しをしようとした時点で、
Segmentation fault: 11
だの、
Fatal Python error: PyEval_SaveThread: NULL tstate
だのいわれて散々でした。
後者のエラーメッセージが検索に引っ掛かり、StackOverFlowの記事にたどり着きました。
だいたいこういうのは複数のバージョンの python が混在してるときじゃね?
otool -Lとかで依存関係を見てみたら;)
とのコメントがまさにビンゴ。確かにOSXネイティブなパスとhomebrewのパスが混在していました。
ということで、-F オプションをつけてフレームワークのパスを OSX ネイティブのものではなく homebrew のものに置き換えることで解決しました。
-Fオプションには man ld でたどり着きました。恥ずかしながら -framework オプションも理解しておらず。勉強不足でしたがどうにかたどり着きました。
参考リンク
- http://d.hatena.ne.jp/moriyoshi/20091214/1260779899
- http://www.boost.org/doc/libs/1_60_0/libs/python/doc/html/tutorial/tutorial/hello.html
- http://alpha.osdn.jp/devel/boost.python_ja.pdf
- http://d.hatena.ne.jp/saket/20111017/1318841837
- http://stackoverflow.com/questions/4985933/is-the-pythreadstate-of-the-main-python-thread-expected-to-be-null
- http://stackoverflow.com/questions/1780133/linking-against-apple-frameworks-with-gcc