pybind11(available in https://github.com/pybind/pybind11) is a lightweight header-only library that exposes C++ types in Python and vice versa, mainly to create Python bindings of existing C++ code
similar project: Boost.python
advantages of pybind compared to Boost.python:
dramatically simpler binding code in many common situations
Include with PyPI
pip install pybind11
// 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");
}
# CMakeLists
find_package(pybind11)
pybind11_add_module(example
example.cpp
)
m.doc()–>对于这一个module的说明
PYBIND11_MODULE(example, m)中example是module的名字,m defines a variable of type
py::module_
which can be used to initialize the module.
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;
};
#include <pybind11/pybind11.h>
namespace py = pybind11;
PYBIND11_MODULE(example, m) {
py::class_<Pet>(m, "Pet")
.def(py::init<const std::string &>())
.def("setName", &Pet::setName)
.def("getName", &Pet::getName);
}
% python
>>> import example
>>> p = example.Pet('Molly')
>>> print(p)
<example.Pet object at 0x10cd98060>
>>> p.getName()
u'Molly'
>>> p.setName('Charly')
>>> p.getName()
u'Charly'
注意到上面的print打印出的内容对于我们掌握pet的信息并没有什么帮助,所以我们可以重写,增加一句
.def("__repr__",
[](const Pet &a) {
return "<example.Pet named '" + a.name + "'>";
}
此时
print(p)
<example.Pet named 'Molly'>
还能在python端给类增加新的属性
py::class_<Pet>(m, "Pet", py::dynamic_attr())
.def(py::init<>())
.def_readwrite("name", &Pet::name);
p = example.Pet()
>>> p.name = 'Charly' # OK, overwrite value in C++
>>> p.age = 2 # OK, dynamically add a new attribute
>>> p.__dict__ # just like a native Python class
{'age': 2}
方法重载
py::class_<Pet>(m, "Pet")
.def("set", py::overload_cast<int>(&Pet::set), "Set the pet's age")
.def("set", py::overload_cast<const std::string &>(&Pet::set), "Set the pet's name");
return value policies(具体说明在官方文档),默认的为automatic
a wrong example
/* Function declaration */
Data *get_data() { return _data; /* (pointer to a static data structure) */ }
...
/* Binding code */
m.def("get_data", &get_data); // <-- KABOOM, will cause crash when called from Python
需要被更改为
m.def("get_data", &get_data, py::return_value_policy::reference);
如果在类中定义了纯虚函数,那么在绑定的时候需要定义一个trampoline class来辅助绑定
范例:
class Animal {
public:
virtual ~Animal() { }
virtual std::string go(int n_times) = 0;
};
class Dog : public Animal {
public:
std::string go(int n_times) override {
std::string result;
for (int i=0; i<n_times; ++i)
result += "woof! ";
return result;
}
};
trampoline class:
class PyAnimal : public Animal {
public:
/* Inherit the constructors */
using Animal::Animal;
/* Trampoline (need one for each virtual function) */
std::string go(int n_times) override {
PYBIND11_OVERRIDE_PURE(
std::string, /* Return type */
Animal, /* Parent class */
go, /* Name of function in C++ (must match Python name) */
n_times /* Argument(s) */
);
}
};
绑定代码:
PYBIND11_MODULE(example, m) {
py::class_<Animal, PyAnimal /* <--- trampoline*/>(m, "Animal")
.def(py::init<>())
.def("go", &Animal::go);
py::class_<Dog, Animal>(m, "Dog")
.def(py::init<>());
m.def("call_go", &call_go);
}
py_heuristic_load_planner中
class ContainerTrampoline
: public Container<TState, TBlockList, TConfigInfo>
{
public:
using base_container_t = Container<TState, TBlockList, TConfigInfo>;
using base_container_t::base_container_t;
void load_all() override
{
PYBIND11_OVERRIDE(void, base_container_t, load_all,
);
}
LoadStatus load_once() override
{
PYBIND11_OVERRIDE_PURE(LoadStatus, base_container_t, load_once,
);
}
};
注意在PYBIND11_OVERRIDE中是只识别逗号的,所以当使用带有多个模板的模板类的时候需要先使用using关键字,再填写否则会报错
pybind11还能够绑定factory function和lamda function
pickling support:
Python’s pickle
module provides a powerful facility to serialize and de-serialize a Python object graph into a binary data stream
class Pickleable {
public:
Pickleable(const std::string &value) : m_value(value) { }
const std::string &value() const { return m_value; }
void setExtra(int extra) { m_extra = extra; }
int extra() const { return m_extra; }
private:
std::string m_value;
int m_extra = 0;
};
py::class_<Pickleable>(m, "Pickleable")
.def(py::init<std::string>())
.def("value", &Pickleable::value)
.def("extra", &Pickleable::extra)
.def("setExtra", &Pickleable::setExtra)
.def(py::pickle(
[](const Pickleable &p) { // __getstate__
/* Return a tuple that fully encodes the state of the object */
return py::make_tuple(p.value(), p.extra());
},
[](py::tuple t) { // __setstate__
if (t.size() != 2)
throw std::runtime_error("Invalid state!");
/* Create a new C++ instance */
Pickleable p(t[0].cast<std::string>());
/* Assign any additional state */
p.setExtra(t[1].cast<int>());
return p;
}
));
std::unique_ptr:
不用做额外处理
std::shared_ptr:
#include <pybind11/pybind11.h>
namespace py = pybind11;
class Child { };
class Parent {
public:
Parent() : child(std::make_shared<Child>()) { }
private:
std::shared_ptr<Child> child;
};
PYBIND11_MODULE(example, m) {
py::class_<Child, std::shared_ptr<Child>>(m, "Child");
py::class_<Parent, std::shared_ptr<Parent>>(m, "Parent")
.def(py::init<>());
}
Reference: https://pybind11.readthedocs.io/en/latest/