#
Write C++ Bindings
#
Quick Start
pkpy provides a pybind11 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
#include <pybind11/pybind11.h>
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
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<int, int>(&add));
// support function overload
m.def("add", py::overload_cast<int, int, int>(&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<int>();
}
return sum;
});
// bind **kwargs
m.def("add", [](py::kwargs kwargs) {
int sum = 0;
for (auto item : kwargs) {
sum += item.second.cast<int>();
}
return sum;
});
}
#
class
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_<Point>(m, "Point")
.def(py::init<>())
.def(py::init<int, int>())
.def(py::init<const Point&>())
.def_readonly("x", &Point::x)
.def_readwrite("y", &Point::y)
.def("__str__", &Point::stringfy);
// only support single inheritance
py::class_<Point3D, Point>(m, "Point3D", py::dynamic_attr())
.def(py::init<int, int, int>())
.def_property("z", &Point3D::get_z, &Point3D::set_z);
// dynamic_attr will enable the dict of bound class
}
#
operators
#include <pybind11/operators.h>
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_<Int>(m, "Int")
.def(py::init<int>())
.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:
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<T>; // cast to C++ type
py::type::of(obj); // get type of obj
py::type::of<T>(); // get type of T, if T is registered
you can also create some builtin objects with their according wrappers:
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 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
- Function overloading
- Return value policy
- is_prepend
-
*args
and**kwargs
- Keep-alive
- Call Guard
- Default arguments
- Keyword-Only arguments
- Positional-Only arguments
- Allow/Prohibiting None arguments
#
Class
- Creating bindings for a custom type
- Binding lambda functions
- Dynamic attributes
- Inheritance and automatic downcasting
- 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
Need further discussion.
#
Smart pointers
- std::shared_ptr
- std::unique_ptr
- Custom smart pointers
#
Type conversions
- Python built-in types
- STL Containers
- Functional
- Chrono
#
Python C++ interface
Need further discussion.
-
object
-
none
-
type
-
bool_
-
int_
-
float_
-
str
-
bytes
-
bytearray
-
tuple
-
list
-
set
-
dict
-
slice
-
iterable
-
iterator
-
function
-
buffer
-
memoryview
-
capsule
#
Miscellaneous
- Global Interpreter Lock (GIL)
- Binding sequence data types, iterators, the slicing protocol, etc.
- Convenient operators binding
#
Differences between CPython and pocketpy
only
add
,sub
andmul
have corresponding right versions in pocketpy. So if you bindint() >> 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.thre return value of
globals
is immutable in pocketpy.