pybind11
install
pip install pybind11bash
example
example.cpp:
// include
include <pybind11/pybind11.h>
namespace py = pybind11;
// bind a simple function
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");
}cpp
compile with:
c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)bash
use in main.py:
import example
example.add(1, 2)python
binding basics
-
functions
int add(int i, int j) { return i + j; } PYBIND11_MODULE(example, m) { m.def("add", &add, "help"); }cppimport example example.add(1, 2)python-
keyword parameters
// regular notation m.def("add1", &add, "help", py::arg("i"), py::arg("j")); // shorthand using namespace pybind11::literals; m.def("add2", &add, "help", "i"_a, "j"_a);cppimport example example.add(i=1, j=2)python -
default parameters
// regular notation m.def("add1", &add, "help", py::arg("i") = 1, py::arg("j") = 2); // shorthand using namespace pybind11::literals; m.def("add2", &add, "help", "i"_a=1, "j"_a=2);cpp
-
-
variables
PYBIND11_MODULE(example, m) { m.attr("the_answer") = 42; py::object world = py::cast("World"); m.attr("what") = world; }cppimport example example.the_answer example.whatpython -
class
// definition 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; }; // binding include <pybind11/pybind11.h> namespace py = pybind11; PYBIND11_MODULE(example, m) { py::class_<Pet>(m, "Pet") // "Pet" is the class name in python .def(py::init<const std::string &>()) // init method .def("setName", &Pet::setName) .def("getName", &Pet::getName); }cppimport example p = example.Pet('Molly') print(p) p.getName() p.setName('Charly') p.getName()python-
binding lambda functions
py::class_<Pet>(m, "Pet") .def(py::init<const std::string &>()) .def("setName", &Pet::setName) .def("getName", &Pet::getName) // the __repr__ using a lambda function .def("__repr__", [](const Pet &a) { return "<example.Pet named '" + a.name + "'>"; } );cpp -
binding attributes
// public attribute py::class_<Pet>(m, "Pet") .def(py::init<const std::string &>()) .def_readwrite("name", &Pet::name) ...cppimport example p = example.Pet('Molly') p.name p.name = "Charly"python// private attribute and const attribute class Pet { public: Pet(const std::string &name) : name(name) { } void setName(const std::string &name_) { name = name_; } const std::string &getName() const { return name; } const int x = 0; private: std::string name; }; py::class_<Pet>(m, "Pet") .def(py::init<const std::string &>()) .def_readonly("x", &Pet::x) .def_property("name", &Pet::getName, &Pet::setName) ...cppimport example p = example.Pet('Molly') p.name p.name = "Charly" # Error!python -
dynamic attribute
By default,
pybind11class doesn't support dynamic attribute like python class:import example p = example.Pet('Molly') p.age = 2 # Attribute Error!pythonTo enable it, use
py::dynamic_attr():py::class_<Pet>(m, "Pet", py::dynamic_attr()) .def(py::init<>()) .def_readwrite("name", &Pet::name); ...cppimport example p = example.Pet('Molly') p.age = 2 # OK!python -
Inheritance
// TBD...cpp -
Overloaded method
// TBD...cpp
-
Misc
- Binding template functions.
template <typename T>
void set(T t);cpp
You cannot bind a template function directly, but you can instantiate it first:
// overload
m.def("set", &set<int>);
m.def("set", &set<std::string>);
// or explicitly bind to different name
m.def("setInt", &set<int>);
m.def("setString", &set<std::string>);
cpp
Type Conversion
Basic types
This includes int, float, bool, ...
Copy is made when conversion between.
python str <--> std::string
Also quite straight-forward, copy is made.
m.def("utf8_test",
[](const std::string &s) {
cout << "utf-8 is icing on the cake.\n";
cout << s;
}
);cpp
utf8_test("🎂")python
list/tuple <--> std::vector/deque/array
list and tuple are not distinguished. Copy is made!
include <pybind11/stl.h>
m.def("cast_vector", []() { return std::vector<int>{1}; });
m.def("load_vector", [](const std::vector<int> &v) { return v.at(0) == 1 && v.at(1) == 2; });cpp
lst = m.cast_vector()
assert lst == [1]
lst.append(2)
assert m.load_vector(lst)
assert m.load_vector(tuple(lst))python
dict <--> std::map
key and value types are automatically handled.
include <pybind11/stl.h>
m.def("cast_map", []() { return std::map<std::string, std::string>{{"key", "value"}}; });
m.def("load_map", [](const std::map<std::string, std::string> &map) {
return map.at("key") == "value" && map.at("key2") == "value2";
});cpp
d = m.cast_map()
assert d == {"key": "value"}
assert "key" in d
d["key2"] = "value2"
assert "key2" in d
assert m.load_map(d)python
set <--> std::set
m.def("cast_set", []() { return std::set<std::string>{"key1", "key2"}; });
m.def("load_set", [](const std::set<std::string> &set) {
return (set.count("key1") != 0u) && (set.count("key2") != 0u) && (set.count("key3") != 0u);
});cpp
s = m.cast_set()
assert s == {"key1", "key2"}
s.add("key3")
assert m.load_set(s)python
numpy.ndarray <--> Eigen Matrix
Pass-by-value are supported between np.ndarray and Eigen::MatrixXd. A copy is made for each conversion, which maybe unwanted.
Pass-by-reference can be achieved through Eigen::Ref<T>.
TODO: https://github.com/pybind/pybind11/blob/master/tests/test_eigen.cpp