Posted at

C++でpythonを拡張する(Boost.NumPy)

More than 5 years have passed since last update.


何故C++のクラス・関数をPythonにバインドするか?

C++11は非常に便利になった。Boost使えばさらに便利だ。

しかし、pythonはもっと使いやすい。

NumPyをベースにしたプロジェクト群(SciPy, matplotlib, pandas, ...)

は共通の基盤の上に非常に使い易く整備されている上に、そこそこ高速に動作する。

C++を使うべきか、Pythonにバインドして使うべきか、

また私の中で結論はでていないが、とりあえずバインドするための情報をまとめる。


どうやってバインドするか?

問題は2つある。


  • 単純にPythonにバインドする方法

  • NumPyにバインドする方法

Pythonを使う理由の一つにNumPyのベースのライブラリを使用する事があるので、

NumPyのndarray等に変換できる必要がある。

基本となるのはPython C APINumPy C APIである。

これらを通してC++の関数やクラスをPythonにバインドする。

C++は当然Python C APIを直接呼びだせるが、

参照カウントを自分で制御しないといけない。

これは無理ゲーすぎるので、その辺をC++らしくラップしたのがBoost.Pythonである。

スマートポインタで参照カウントは管理してくれる。

一方で、NumPy C APIはPython C APIとは別物であるため、

Boost.Python自体にはNumPyのAPIのラッパーは含まれていない。

そこでBoost.PythonをベースにNumPyのためのインターフェイスを

実装したものがBoost.NumPyである。

現在Boost sandboxに入ってると書いているが、

Boost本体の開発がGithubに移ってからSandboxは運用されていないようだ。

開発はgithubで行われている。

Boostとしての立ち位置はよく分らない。


Install

git clone https://github.com/ndarray/Boost.NumPy

cd Boost.NumPy

した事前提で話を進める。


documentのビルド

cd libs/numpy/doc

make

としてlibs/numpy/doc/_build/html/index.htmlを開けばドキュメントが見れる


cmakeによるインストール

上述のドキュメントに詳しくある(略)

python3環境でコンパイルするには少し変更が必要となる。


使い方

SciPyの各種アルゴリズムを使うためには

numpy.ndarray を引数にして同じndarrayを返す関数が必須。

NumPyの肝であるndarrayをC++で作る

project/CMakeLists.txt

numpy.cpp


numpy.cpp

#include "boost/numpy.hpp"


namespace p = boost::python;
namespace np = boost::numpy;

np::ndarray new_zero1(unsigned int N) {
p::tuple shape = p::make_tuple(N);
np::dtype dtype = np::dtype::get_builtin<double>();
return np::zeros(shape, dtype);
}

BOOST_PYTHON_MODULE(mymodule) {
Py_Initialize();
np::initialize();

p::def("new_zero", new_zero1);
}


こんな感じで書く。かなり直感的に書ける。


CMakeLists.txt

cmake_minimum_required(VERSION 2.8)

# set(CMAKE_VERBOSE_MAKEFILE 1)

find_package(Boost COMPONENTS python3 REQUIRED) # for python3
find_package(PythonLibs REQUIRED)
include_directories(${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS})

add_library(mymodule SHARED numpy.cpp)
target_link_libraries(mymodule ${Boost_LIBRARIES} ${PYTHON_LIBRARY} boost_numpy)
set_target_properties(mymodule PROPERTIES PREFIX "") # 接頭辞'lib'を省略するため


cmake .

make

これでmymodule.soができる。

set_target_propertiesを書かないとlibmymodule.soとなる。

LIBRARYだったりLIBRARIESだったりするので注意する。

もっと詳しい使い方は次回。