当前位置: 首页 > 工具软件 > Tutorial > 使用案例 >

Pybind11 tutorial

夹谷英杰
2023-12-01

pybind core features

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

  • Functions accepting and returning custom data structures per value, reference, or pointer
// 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.

  • Object-oriented code
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");
  • Functions

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);
  • Class

如果在类中定义了纯虚函数,那么在绑定的时候需要定义一个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;
        }
    ));
  • Smart pointers

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/

 类似资料:

相关阅读

相关文章

相关问答