Open3DやDlibのソースコードを読んでいるとpybind11という何やらすてきなものを使ってPythonのライブラリを作っているので試してみた。
準備
cmakeを使用します。pybind11はヘッダーだけのライブラリで、github (https://github.com/pybind/pybind11/releases) などからダウンロードして以下のようなディレクトリ配置にします。
project top
|- CMakeLists.txt
|- pybind11/
|- mymodule.cpp
ソースコード
mymodule.cpp
には以下のように書きます。
#include <pybind11/pybind11.h>
int add(int a, int b)
{
return a + b;
}
PYBIND11_MODULE(mymodule, m)
{
m.doc() = "my test module";
m.def("add", &add, "add two numbers");
}
PYBIND11_MODULE
というマクロを使用してPythonモジュールを作成できます。引数の1つめのmymodule
はモジュールの名前で最終的に作成するライブラリのファイル名と揃える必要があります。 (mymodule.cpython-37m-x86_64-linux-gnu.so
のような名前になる場合は mymodule
)
2つ目の引数 m
はマクロ内でのみ使用するもので、その後のように m.def
を使用して関数を登録します。
これをビルドする際には次のような CMakeLists.txt
を作成します。
cmake_minimum_required(VERSION 3.2)
project(pybind_test VERSION 0.1.0)
add_subdirectory(pybind11)
pybind11_add_module(mymodule mymodule.cpp)
このままビルドするとデフォルトのPythonが使用されます。vritualenvやcondaで作った特定のバージョンのPython環境に対してビルドしたい場合は事前にその環境をactivateするか、 cmake -DPYTHON_EXECUTABLE=path/to/python ..
のように指定します。 LinuxでPython3.7に対してビルドするとmymodule.cpython-37m-x86_64-linux-gnu.so
というファイルができます。これをPYTHONPATHが通っている場所(例えばカレントディレクトリ)におけばimportできます。
import mymodule
mymodule.add(1, 10)
>> 11
ソースファイルの分割
プロジェクトが大きくなったり、もともとあるC/C++のコードを利用したいというときに全てのコードを mymodule.cpp
に書くわけには行きません。ソースコードを複数に分割する場合には次のようにします。
project top
|- CMakeLists.txt
|- pybind11/
|- mymodule.cpp
|- lib.cpp
|- lib.hpp
int add(int a, int b);
#include "lib.hpp"
int add(int a, int b)
{
return a + b;
}
#include <pybind11/pybind11.h>
#include "lib.hpp"
PYBIND11_MODULE(mymodule, m)
{
m.doc() = "my test module";
m.def("add", &add, "add two numbers");
}
cmake_minimum_required(VERSION 3.2)
project(pybind_test VERSION 0.1.0)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
add_subdirectory(pybind11)
add_library(test lib.cpp)
pybind11_add_module(mymodule mymodule.cpp)
target_link_libraries(mymodule PRIVATE test)
ポイントはadd_library
を使用してPythonと関係ないライブラリを作成したあと、target_link_libraries
でpybind11で作ったmoduleとそれを結合します。もちろんpybind11_add_module
で全てのソースファイルを指定しても良いのですが、こちらのほうが通常のC/C++用ライブラリとPython用ライブラリの両方ができるので何かと便利だと思います。