其他方法安装可以参考:https://pybind11.readthedocs.io/en/stable/installing.html
本次采用了Bazel作为管理器,因此在安装的时候只需要在third_party中构建对应的bzl的rule即可
"""Loads the pybind11 library"""
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
def repo():
http_archive(
name = "pybind11_bazel",
sha256 = "8f546c03bdd55d0e88cb491ddfbabe5aeb087f87de2fbf441391d70483affe39",
strip_prefix = "pybind11_bazel-26973c0ff320cb4b39e45bc3e4297b82bc3a6c09",
urls = [
"https://qcraft-web.oss-cn-beijing.aliyuncs.com/cache/packages/pybind11_bazel-26973c0ff320cb4b39e45bc3e4297b82bc3a6c09.tar.gz",
"https://github.com/pybind/pybind11_bazel/archive/26973c0ff320cb4b39e45bc3e4297b82bc3a6c09.tar.gz",
],
)
http_archive(
name = "pybind11",
build_file = "@pybind11_bazel//:pybind11.BUILD",
sha256 = "6cd73b3d0bf3daf415b5f9b87ca8817cc2e2b64c275d65f9500250f9fee1677e",
strip_prefix = "pybind11-2.7.0",
urls = [
"https://qcraft-web.oss-cn-beijing.aliyuncs.com/cache/packages/pybind11-2.7.0.tar.gz",
"https://github.com/pybind/pybind11/archive/refs/tags/v2.7.0.tar.gz",
],
)
#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");
}
返回值策略 | 描述 |
---|---|
return_value_policy::take_ownership | 引用现有对象(不创建一个新对象),并获取所有权。在引用计数为0时,Pyhton将调用析构函数和delete操作销毁对象。 |
return_value_policy::copy | 拷贝返回值,这样Python将拥有拷贝的对象。该策略相对来说比较安全,因为两个实例的生命周期是分离的。 |
return_value_policy::move | 使用std::move 来移动返回值的内容到新实例,新实例的所有权在Python。该策略相对来说比较安全,因为两个实例的生命周期是分离的。 |
return_value_policy::reference | 引用现有对象,但不拥有所有权。C++侧负责该对象的生命周期管理,并在对象不再被使用时负责析构它。注意:当Python侧还在使用引用的对象时,C++侧删除对象将导致未定义行为。 |
return_value_policy::reference_internal | 返回值的生命周期与父对象的生命周期相绑定,即被调用函数或属性的this 或self 对象。这种策略与reference策略类似,但附加了keep_alive<0, 1> 调用策略保证返回值还被Python引用时,其父对象就不会被垃圾回收掉。这是由def_property 、def_readwrite 创建的属性getter方法的默认返回值策略。 |
return_value_policy::automatic | 当返回值是指针时,该策略使用return_value_policy::take_ownership 。反之对左值和右值引用使用return_value_policy::copy 。请参阅上面的描述,了解所有这些不同的策略的作用。这是py::class_ 封装类型的默认策略。 |
return_value_policy::automatic_reference | 和上面一样,但是当返回值是指针时,使用return_value_policy::reference 策略。这是在C++代码手动调用Python函数和使用pybind11/stl.h 中的casters时的默认转换策略。你可能不需要显式地使用该策略。 |
使用方法,类似这样:m.def("get_data", &get_data, py::return_value_policy::reference);
void print_dict(const py::dict& dict) {
/* Easily interact with Python types */
for (auto item : dict)
std::cout << "key=" << std::string(py::str(item.first)) << ", "
<< "value=" << std::string(py::str(item.second)) << std::endl;
}
// it can be exported as follow:
m.def("print_dict", &print_dict);
>>> print_dict({"foo": 123, "bar": "hello"})
key=foo, value=123
key=bar, value=hello
// 有个类型转换的过程
void print_list(const std::vector<int>& input) {
/* Easily interact with Python types */
for (auto in : input) std::cout << in << std::endl;
}
void print_list_string(const std::vector<std::string>& input) {
/* Easily interact with Python types */
for (auto in : input) std::cout << in << std::endl;
}
// 这里是吧原生的python类型包在了py::object中
void print_listv2(py::list my_list) {
for (auto item : my_list)
std::cout << item << " ";
}
m.def("print_list", &print_list);
m.def("print_list_string", &print_list_string);
m.def("print_listv2", &print_listv2);
py::arg("key_word_name")
来绑定参数的关键字m.def("add", &add, "A function which adds two numbers",
py::arg("i"), py::arg("j"));
import example
example.add(i=1, j=2) #3L
_a
会生成一个等价于arg
方法的字面量using namespace pybind11::literals
来声明后缀所在的命名空间。这样除了literals
外,不会从pybind11命名空间引入其他不必要的东西。// regular notation
m.def("add1", &add, py::arg("i"), py::arg("j"));
// shorthand
using namespace pybind11::literals;
m.def("add2", &add, "i"_a, "j"_a);
py::class_<MyClass>("MyClass").def("myFunction", py::arg("arg") = SomeType(123));
// regular notation
py::class_<MyClass>("MyClass").def("myFunction", py::arg("arg") = SomeType(123));
// shorthand
py::class_<MyClass>("MyClass").def("myFunction", "arg"_a = SomeType(123));
m.def("floats_only", [](double f) { return 0.5 * f; }, py::arg("f").noconvert());
m.def("floats_preferred", [](double f) { return 0.5 * f; }, py::arg("f"));
py::arg
对象的.none
方法来显式地使能或禁止该行为py::class_<Dog>(m, "Dog").def(py::init<>());
py::class_<Cat>(m, "Cat").def(py::init<>());
m.def("bark", [](Dog *dog) -> std::string {
if (dog) return "woof!"; /* Called with a Dog instance */
else return "(no dog)"; /* Called with None, dog == nullptr */
}, py::arg("dog").none(true));
m.def("meow", [](Cat *cat) -> std::string {
// Can't be called with None argument
return "meow";
}, py::arg("cat").none(false));
# python
>>> from animals import Dog, Cat, bark, meow
>>> bark(Dog())
'woof!'
>>> meow(Cat())
'meow'
>>> bark(None)
'(no dog)'
>>> meow(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: meow(): incompatible function arguments. The following argument types are supported:
1. (cat: animals.Cat) -> str
Invoked with: None
template <typename T>
T add(T& a, T& b) //这就是一个模板函数
{
return a + b;
}
m.def("add", &add<int>, "A function which adds two numbers");
m.def("add", &add<double>, "A function which adds two numbers");
m.def("add", &add<float>, "A function which adds two numbers");
#include <pybind11/pybind11.h>
namespace py = pybind11;
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;
};
PYBIND11_MODULE(example, m) {
py::class_<Pet>(m, "Pet")
.def(py::init<const std::string &>())
.def("setName", &Pet::setName)
.def("getName", &Pet::getName)
.def_readwrite("name", &Pet::name);
// 当然上面的可以用def_property替代
// .def_property("name", &Pet::getName, &Pet::setName)
}
# 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'
class_
的模板参数;2)将基类名作为class_
的参数绑定到派生类。两种方法是等效的。struct Pet {
Pet(const std::string &name) : name(name) { }
std::string name;
};
struct Dog : Pet {
Dog(const std::string &name) : Pet(name) { }
std::string bark() const { return "woof!"; }
};
py::class_<Pet>pet(m, "Pet");
pet.def(py::init<const std::string &>())
.def_readwrite("name", &Pet::name);
// Method 1: template parameter:
py::class_<Dog, Pet /* <- specify C++ parent type */>(m, "Dog")
.def(py::init<const std::string &>())
.def("bark", &Dog::bark);
// Method 2: pass parent class_ object:
py::class_<Dog>(m, "Dog", pet /* <- specify Python parent type */)
.def(py::init<const std::string &>())
.def("bark", &Dog::bark);
struct Pet {
Pet(const std::string &name, int age) : name(name), age(age) { }
void set(int age_) { age = age_; }
void set(const std::string &name_) { name = name_; }
std::string name;
int age;
};
py::class_<Pet>(m, "Pet")
.def(py::init<const std::string &, int>())
.def("set", static_cast<void (Pet::*)(int)>(&Pet::set), "Set the pet's age")
.def("set", static_cast<void (Pet::*)(const std::string &)>(&Pet::set), "Set the pet's name");
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");
// c++ 11 中使用py::detail::overload_cast_impl替代
template <typename... Args>
using overload_cast_ = pybind11::detail::overload_cast_impl<Args...>;
py::class_<Pet>(m, "Pet")
.def("set", overload_cast_<int>()(&Pet::set), "Set the pet's age")
.def("set", overload_cast_<const std::string &>()(&Pet::set), "Set the pet's name");
py::const
标识。struct Widget {
int foo(int x, float y);
int foo(int x, float y) const;
};
py::class_<Widget>(m, "Widget")
.def("foo_mutable", py::overload_cast<int, float>(&Widget::foo))
.def("foo_const", py::overload_cast<int, float>(&Widget::foo, py::const_));
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;
}
};
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) */
);
}
};
// Animal类的绑定代码也需要一些微调:
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);
}
// pybind11通过向`class_`指定额外的模板参数PyAnimal,让我们可以在Python中继承Animal类。
PYBIND11_OVERRIDE_PURE
宏,而有默认实现的虚函数则使用PYBIND11_OVERRIDE
。PYBIND11_OVERRIDE_PURE_NAME
和PYBIND11_OVERRIDE_NAME
宏的功能类似,主要用于C函数名和Python函数名不一致的时候。以__str__
为例:std::string toString() override {
PYBIND11_OVERRIDE_NAME(
std::string, // Return type (ret_type)
Animal, // Parent class (cname)
"__str__", // Name of method in Python (name)
toString, // Name of function in C++ (fn)
);
}
struct Pet {
enum Kind {
Dog = 0,
Cat
};
struct Attributes {
float age = 0;
};
Pet(const std::string &name, Kind type) : name(name), type(type) { }
std::string name;
Kind type;
Attributes attr;
};
py::class_<Pet> pet(m, "Pet");
pet.def(py::init<const std::string &, Pet::Kind>())
.def_readwrite("name", &Pet::name)
.def_readwrite("type", &Pet::type)
.def_readwrite("attr", &Pet::attr);
py::enum_<Pet::Kind>(pet, "Kind")
.value("Dog", Pet::Kind::Dog)
.value("Cat", Pet::Kind::Cat)
.export_values();
py::class_<Pet::Attributes> attributes(pet, "Attributes")
.def(py::init<>())
.def_readwrite("age", &Pet::Attributes::age);
attr
函数来注册需要导出到Python模块中的C++变量。PYBIND11_MODULE(example, m) {
m.attr("the_answer") = 42;
py::object world = py::cast("World");
m.attr("what") = world;
}
``
Python中使用如下:
```pyhton
>>> import example
>>> example.the_answer
42
>>> example.what
'World'
std::unique_ptr
,给出的解释是:如果用智能指针作为参数在传递的时候就意味着Python放弃所有权,而由于该对象可能在Python中别的地方被引用,这通常是不行的。std::shared_ptr
也无法当做参数传递,当然也可能是个人没有测试到位。std::unique_ptr< type >
也就是说,,这意味着当Python的引用计数变为0时将释放该对象。
std::shared_ptr
:py::class_<Example, std::shared_ptr<Example> /* <- holder type */> obj(m, "Example");