C++ を Python から利用する方法の一つに pybind11 があります。C++11 をサポートするコンパイラが必要です。サンプルコードを記載します。
C++11 コードをビルドして、Python から利用するための .so
ファイルを生成します。ビルド方法は複数ありますがここでは CMake を利用します。
Building with CMake、参考: git submodule
$ git init
$ git submodule add https://github.com/pybind/pybind11.git
$ mkdir build
$ ls
CMakeLists.txt build example.cpp main.py pybind11
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 2.8.12)
project(example)
add_subdirectory(pybind11)
pybind11_add_module(example example.cpp)
$ cat example.cpp
#include <pybind11/pybind11.h>
int add(int i, int j) {
return i + j;
}
PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("add", &add, "A function which adds two numbers");
}
$ cat main.py
import example
print example.add(1, 1)
example.so
のビルド
cd build
cmake -DPYTHON_EXECUTABLE=`which python` ..
make
実行例
$ PYTHONPATH=$PYTHONPATH:build python ../main.py
2
インストールされている Python のバージョンは CMake が自動的に検出します。検出された情報は CMakeCache.txt
内に記載されます。
$ grep python CMakeCache.txt | head -1
PYTHON_EXECUTABLE:FILEPATH=/path/to/python3.6
複数バージョンの Python が存在する環境においては、利用する Python 実行ファイルへのパスを指定します。
cmake -DPYTHON_EXECUTABLE=/usr/bin/python2 ..
cmake -DPYBIND11_PYTHON_VERSION=2.7 ..
CMakeLists.txt
内で指定することもできます。
set(PYBIND11_PYTHON_VERSION 2.7)
または
set(PYTHON_EXECUTABLE /usr/bin/python2)
add_subdirectory
より前に指定します。
set(PYBIND11_CPP_STANDARD -std=c++11)
または
set(PYBIND11_CPP_STANDARD -std=c++14)
int add(int i, int j) {
return i + j;
}
m.def("add", &add);
参考: 名前空間 (C++をもう一度)
namespace py = pybind11;
m.def("add", &add, py::arg("i"), py::arg("j"));
または
namespace py = pybind11;
using namespace py::literals;
m.def("add", &add, "i"_a, "j"_a);
Python
print example.add(i=1, j=1)
namespace py = pybind11;
m.def("add", &add, py::arg("i") = 1, py::arg("j") = 1);
または
namespace py = pybind11;
using namespace py::literals;
m.def("add", &add, "i"_a=1, "j"_a=1);
Python
print example.add()
m.attr("xxx") = 123;
namespace py = pybind11;
py::object yyy = py::cast("mystr");
m.attr("yyy") = yyy;
Python
print example.xxx
print example.yyy
example.cpp
#include <pybind11/pybind11.h>
struct Pet {
Pet(const std::string &name) : name(name) { }
void setName(const std::string &name_) { name = name_; }
const std::string &getName() const { return name; }
std::string name;
};
namespace py = pybind11;
PYBIND11_MODULE(example, m) {
py::class_<Pet>(m, "Pet") // 構造体であっても class_ でバインディングします。
.def(py::init<const std::string &>()) // コンストラクタの引数です。
.def("setName", &Pet::setName)
.def("getName", &Pet::getName);
}
main.py
import example
p = example.Pet('xxx')
print p
print p.getName()
p.setName('yyy')
print p.getName()
name(name)
は std::string
のコンストラクタを利用した、メンバ変数の初期化です → 継承における引数のあるコンストラクタ
getName() const
は const メンバ関数です → クラスの基本/constオブジェクトとconstメンバ関数 (C++をもう一度)
setName
および getName
はインライン関数です → インライン関数 (C++をもう一度)
静的メンバ関数をバインディングするためには def
ではなく def_static
を利用します。
example.cpp
#include <pybind11/pybind11.h>
#include <iostream>
class MyClass {
public:
static void Show() { std::cout << m_mystr << std::endl; }
private:
static std::string m_mystr;
};
std::string MyClass::m_mystr = "hello";
PYBIND11_MODULE(example, m) {
pybind11::class_<MyClass>(m, "MyClass")
.def_static("Show", &MyClass::Show);
}
main.py
import example
example.MyClass.Show() #=> hello
メンバ変数
.def_readwrite("name", &Pet::name)
.def_readonly("name", &Pet::name)
private メンバ変数
.def_property("name", &Pet::getName, &Pet::setName)
private メンバ定数
.def_property_readonly("name", &Pet::getName)
静的メンバ変数および静的メンバ定数のバインディングには以下を利用します。
def_readwrite_static()
def_readonly_static()
def_property_static()
def_property_readonly_static()
#include <pybind11/pybind11.h>
struct MyClass {
MyClass(const std::string &name) : name(name) { }
const std::string getName() const { return name; }
std::string name;
};
struct MySubClass : MyClass {
MySubClass(const std::string &name) : MyClass(name) { }
};
PYBIND11_MODULE(example, m) {
pybind11::class_<MyClass>(m, "MyClass")
.def(pybind11::init<const std::string &>())
.def("getName", &MyClass::getName)
.def_readwrite("name", &MyClass::name);
pybind11::class_<MySubClass, MyClass>(m, "MySubClass")
.def(pybind11::init<const std::string &>());
// あるいは以下のようにもできます
// myClass.def(pybind11::init<const std::string &>())
// .def("getName", &MyClass::getName)
// .def_readwrite("name", &MyClass::name);
// pybind11::class_<MySubClass>(m, "MySubClass", myClass)
// .def(pybind11::init<const std::string &>());
}
Python
import example
sub = example.MySubClass('myname')
print sub.name #=> myname
print sub.getName() #=> myname
C++ 側で実装されていない関数をラムダ式で定義してバインディングしたり、C++ における実装をラムダ式で拡張して同名でバインディングしたりできます。Python の __repr__
を実装する場合にも使うことができます。
.def("__repr__",
[](const Pet &a) {
return "<example.Pet named '" + a.name + "'>";
}
);
関数ポインタにキャストすることでオーバーロードされた関数を pybind できます。
#include <pybind11/pybind11.h>
#include <iostream>
void f(int i) {
std::cout << "f: " << i << std::endl;
}
void f(const std::string &s) {
std::cout << "f2: " << s << std::endl;
}
PYBIND11_MODULE(example, m) {
m.def("f", (void (*)(int)) &f)
.def("f", (void (*)(const std::string &)) &f);
}
Python
import example
example.f(123) #=> f: 123
example.f('aaa') #=> f2: aaa
C++14 に対応したコンパイラの場合は pybind11::overload_cast
が利用できます。
m.def("f", pybind11::overload_cast<int>(&f))
.def("f", pybind11::overload_cast<const std::string &>(&f));
単純な列挙体は以下のように pybind します。
#include <pybind11/pybind11.h>
enum MyEnum {
VALUE_A,
VALUE_B,
VALUE_C
};
PYBIND11_MODULE(example, m) {
pybind11::enum_<MyEnum>(m, "MyEnum")
.value("VALUE_A", MyEnum::VALUE_A)
.value("VALUE_B", MyEnum::VALUE_B)
.value("VALUE_C", MyEnum::VALUE_C);
}
Python
import example
b = example.MyEnum.VALUE_B
int(b) #=> 1
b.name #=> u'VALUE_B'
In [11]: example.MyEnum.__members__
Out[11]:
{u'VALUE_A': MyEnum.VALUE_A,
u'VALUE_B': MyEnum.VALUE_B,
u'VALUE_C': MyEnum.VALUE_C}
クラス内の列挙体は以下のようにします。
#include <pybind11/pybind11.h>
struct Pet {
enum Kind {
Dog = 0,
Cat
};
Pet(const std::string &name, Kind type) : name(name), type(type) { }
std::string name;
Kind type;
};
PYBIND11_MODULE(example, m) {
pybind11::class_<Pet> pet(m, "Pet");
pet.def(pybind11::init<const std::string &, Pet::Kind>())
.def_readwrite("name", &Pet::name)
.def_readwrite("type", &Pet::type);
pybind11::enum_<Pet::Kind>(pet, "Kind")
.value("Dog", Pet::Kind::Dog)
.value("Cat", Pet::Kind::Cat)
.export_values();
}
Python
from example import Pet
p = Pet('Lucy', Pet.Cat)
print p.type #=> Kind.Cat
print p.type.name #=> u'Cat'
print int(p.type) #=> 1
print Pet.Kind.__members__ #=> {u'Dog': Kind.Dog, u'Cat': Kind.Cat}
#include <pybind11/pybind11.h>
#include <memory>
#include <iostream>
struct MyClass {
~MyClass() {
std::cout << "myclass destroyed" << std::endl;
}
};
std::unique_ptr<MyClass> createMyClass() {
return std::unique_ptr<MyClass>(new MyClass());
}
PYBIND11_MODULE(example, m) {
pybind11::class_<MyClass>(m, "MyClass")
.def(pybind11::init<>());
m.def("createMyClass", &createMyClass);
}
Python
from example import createMyClass
obj = createMyClass()
print 'obj exists'
obj = None
print 'obj is none'
実行例
obj exists
myclass destroyed
obj is none
#include <pybind11/pybind11.h>
#include <memory>
#include <iostream>
class Shared {
public:
~Shared() {
std::cout << "shared destroyed" << std::endl;
}
};
class MyClass {
public:
MyClass() : shared(std::make_shared<Shared>()) { }
~MyClass() {
std::cout << "myclass destroyed" << std::endl;
}
std::shared_ptr<Shared> getShared() { return shared; }
private:
std::shared_ptr<Shared> shared;
};
PYBIND11_MODULE(example, m) {
pybind11::class_<Shared, std::shared_ptr<Shared>>(m, "Shared");
pybind11::class_<MyClass, std::shared_ptr<MyClass>>(m, "MyClass")
.def(pybind11::init<>())
.def("getShared", &MyClass::getShared);
}
Python
from example import MyClass
shared = MyClass().getShared()
print 'shared exists'
shared = None
print 'shared none'
実行例
myclass destroyed
shared exists
shared destroyed
shared none