Pybind11 Type Conversions

Pybind11类型转换

Pybind11帮助我们方便的实现C++和Python之间的调用,无论是相互的接口暴露还是数据传递都要求我们正确的在C++和Python类型之间进行转换(官方文档)。

三种场景

  1. C++原生类型暴露给python
  2. Python原生类型暴露给C++
  3. C++和Python原生类型间相互转换

C++原生类型暴露给Python

使用py::class_将自定义的C++类型暴露给Python(参考这里)。C++类型传递给Python时会在原生C++类型外加一层wrapper,从Python取回时只用去掉wrapper即可。

Python原生类型暴露给C++

在C++中取用Python的原生类型(例如:tuplelist),例如:

1
2
3
4
5
6
// C++中使用Python对象
void print_list(py::list my_list) {
for (auto item : my_list) {
std::cout << item << " ";
}
}

Python中传入Python对象给C++:

1
2
>>> print_list([1, 2, 3])
1 2 3

这个例子里Python中的list类型没有进行任何转换,只是被封装进了C++的py::list类中。目前Pybind11支持以下类型(参考这里):handle, object, bool_, int_, float_, str, bytes, tuple, list, dict, slice, none, capsule, iterable, iterator, function, buffer, array, array_t

C++和Python原生类型间相互转换

有些场景C++和Python都用的是各自的原生类型,Pybind11支持常见C++原生类型和Python原生类型间的相互转换,例如:

1
2
3
4
5
void print_vector(const std::vector<int> &v) {
for (auto item : v) {
std::cout << item << "\n";
}
}

Python中调用:

1
2
>>> print_vector([1, 2, 3])
1 2 3

容易注意到Python中传入的为list类型,而C++中处理的类型为const std::vector<int> &。Pybind11这种默认类型转换会在原生类型间拷贝数据,例如上面Python中的调用会首先将list中的数据拷贝到一个std::vector<int>中,然后进行C++中的调用。

NOTE: 默认的拷贝可能开销很大,可以通过手写opaque types重载类型转换的wrapper来避免不必要的拷贝。opaque types还没有使用过,有机会单独挖坑写。

STL库

Pybind11默认(pybind11/pybind11.h)支持std::pair<>std::tuple<>list, set, dict之间的自动转换。引入pybind11/stl.h可以新增std::vector<>, std::deque<>, std::list<>, std::array<>, std::set<>, std::unordered_set<>, std::map<>std::unordered_map<>的与Python的自动转换。

绑定STL容器

pybind11/stl_bind.h可以帮助我们将STL容器作为原生对象暴露出来。例如:

1
2
3
4
5
6
7
8
9
10
11
12
#include <pybind11/stl_bind.h>

PYBIND11_MAKE_OPAQUE(std::vector<int>);
PYBIND11_MAKE_OPAQUE(std::map<std::string, double>);

// ...

// 绑定
py::bind_vector<std::vector<int>>(m, "VectorInt");
py::bind_map<std::map<std::string, double>>(m, "MapStringDouble");
// 或者同时设置绑定的作用域
py::bind_vector<std::vector<int>>(m, "VectorInt", py::module_local(false));
/!-- -->