--- icon: cpu title: Write C++ Bindings order: 17 --- ## Quick Start pkpy provides a [pybind11](https://pybind11.readthedocs.io/en/stable/) compatible layer which allows users to do convenient bindings. To begin with, use `py::scoped_interpreter guard{}` to start the interpreter before using any Python objects. Or explicitly call `py::interpreter::initialize()` and `py::interpreter::finalize()`. ### module ```cpp #include namespace py = pybind11; PYBIND11_EMBEDDED_MODULE(example, m) { m.def("add", [](int a, int b) { return a + b; }); auto math = m.def_submodule("math"); } ``` ### function ```cpp int add(int a, int b) { return a + b; } int add(int a, int b, int c) { return a + b + c; } void register_function(py::module_& m) { m.def("add", py::overload_cast(&add)); // support function overload m.def("add", py::overload_cast(&add)); // bind with default arguments m.def("sub", [](int a, int b) { return a - b; }, py::arg("a") = 1, py::arg("b") = 2); // bind *args m.def("add", [](py::args args) { int sum = 0; for (auto& arg : args) { sum += arg.cast(); } return sum; }); // bind **kwargs m.def("add", [](py::kwargs kwargs) { int sum = 0; for (auto item : kwargs) { sum += item.second.cast(); } return sum; }); } ``` ### class ```cpp struct Point { const int x; int y; public: Point() : x(0), y(0) {} Point(int x, int y) : x(x), y(y) {} Point(const Point& p) : x(p.x), y(p.y) {} std::string stringfy() const { return "(" + std::to_string(x) + ", " + std::to_string(y) + ")"; } }; struct Point3D : Point { private: int z; public: Point3D(int x, int y, int z) : Point(x, y), z(z) {} int get_z() const { return z; } void set_z(int z) { this->z = z; } }; void bind_class(py::module_& m) { py::class_(m, "Point") .def(py::init<>()) .def(py::init()) .def(py::init()) .def_readonly("x", &Point::x) .def_readwrite("y", &Point::y) .def("__str__", &Point::stringfy); // only support single inheritance py::class_(m, "Point3D", py::dynamic_attr()) .def(py::init()) .def_property("z", &Point3D::get_z, &Point3D::set_z); // dynamic_attr will enable the dict of bound class } ``` ### operators ```cpp #include namespace py = pybind11; struct Int { int value; Int(int value) : value(value) {} Int operator+(const Int& other) const { return Int(value + other.value); } Int operator-(const Int& other) const { return Int(value - other.value); } bool operator==(const Int& other) const { return value == other.value; } bool operator!=(const Int& other) const { return value != other.value; } }; void bind_operators(py::module_& m) { py::class_(m, "Int") .def(py::init()) .def(py::self + py::self) .def(py::self - py::self) .def(py::self == py::self) .def(py::self != py::self); // other operators are similar } ``` ### py::object `py::object` is just simple wrapper around `PyVar`. It supports some convenient methods to interact with Python objects. here are some common methods: ```cpp obj.attr("x"); // access attribute obj[1]; // access item obj.is_none(); // same as obj is None in Python obj.is(obj2); // same as obj is obj2 in Python // operators obj + obj2; // same as obj + obj2 in Python // ... obj == obj2; // same as obj == obj2 in Python // ... obj(...); // same as obj.__call__(...) py::cast(obj); // cast to Python object obj.cast; // cast to C++ type py::type::of(obj); // get type of obj py::type::of(); // get type of T, if T is registered ``` you can also create some builtin objects with their according wrappers: ```cpp py::bool_ b = {true}; py::int_ i = {1}; py::float_ f = {1.0}; py::str s = {"hello"}; py::list l = {1, 2, 3}; py::tuple t = {1, 2, 3}; // ... ``` ## More Examples More examples please see the test [folder](https://github.com/pocketpy/gsoc-2024-dev/tree/main/pybind11/tests) in the GSoC repository. All tested features are supported. ## Limits and Comparison This is a feature list of pybind11 for pocketpy. It lists all completed and pending features. It also lists the features that cannot be implemented in the current version of pocketpy. ### [Function](https://pybind11.readthedocs.io/en/stable/advanced/functions.html) - [x] Function overloading - [x] Return value policy - [x] is_prepend - [x] `*args` and `**kwargs` - [ ] Keep-alive - [ ] Call Guard - [x] Default arguments - [ ] Keyword-Only arguments - [ ] Positional-Only arguments - [ ] Allow/Prohibiting None arguments ### [Class](https://pybind11.readthedocs.io/en/stable/classes.html) - [x] Creating bindings for a custom type - [x] Binding lambda functions - [x] Dynamic attributes - [x] Inheritance and automatic downcasting - [x] Enumerations and internal types - [ ] Instance and static fields > Binding static fields may never be implemented in pocketpy because it requires a metaclass, which is a heavy and infrequently used feature. ### [Exceptions](https://pybind11.readthedocs.io/en/stable/advanced/exceptions.html) Need further discussion. ### [Smart pointers](https://pybind11.readthedocs.io/en/stable/advanced/smart_ptrs.html) - [ ] std::shared_ptr - [ ] std::unique_ptr - [ ] Custom smart pointers ### [Type conversions](https://pybind11.readthedocs.io/en/stable/advanced/cast/index.html) - [x] Python built-in types - [x] STL Containers - [ ] Functional - [ ] Chrono ### [Python C++ interface](https://pybind11.readthedocs.io/en/stable/advanced/pycpp/object.html) Need further discussion. - [x] `object` - [x] `none` - [x] `type` - [x] `bool_` - [x] `int_` - [x] `float_` - [x] `str` - [ ] `bytes` - [ ] `bytearray` - [x] `tuple` - [x] `list` - [ ] `set` - [x] `dict` - [ ] `slice` - [x] `iterable` - [x] `iterator` - [ ] `function` - [ ] `buffer` - [ ] `memoryview` - [x] `capsule` ### [Miscellaneous](https://pybind11.readthedocs.io/en/stable/advanced/misc.html) - [ ] Global Interpreter Lock (GIL) - [ ] Binding sequence data types, iterators, the slicing protocol, etc. - [x] Convenient operators binding ### Differences between CPython and pocketpy - only `add`, `sub` and `mul` have corresponding right versions in pocketpy. So if you bind `int() >> py::self`, it will has no effect in pocketpy. - `__new__` and `__del__` are not supported in pocketpy. - in-place operators, such as `+=`, `-=`, `*=`, etc., are not supported in pocketpy. - the return value of `globals` is immutable in pocketpy.