Making opaque types
为了处理这种情况,pybind11 提供了PYBIND11_MAKE_OPAQUE(T)
的宏,这个宏使得类型转换不依赖于template-based conversion machinery of types。
这样一来是他们不透明。 opaque types 对象are never inspected or extracted, 因而可以通过引用进行传递。如将std::vector<int>
转为opaque类型, 在编写binding code前,先添加如下声明:
PYBIND11_MAKE_OPAQUE(std::vector<int>)
这个宏必须在程序顶部进行声明,并且不能在namespcae当中,因为这句宏会对模板的初始化进行重载,如果有多个编译单元,那么需要在每个源文件中使用std::vector<int>
前都要有这句宏,通常是共用一个头文件。
通常还会有一个class 来bind相关的operation,以便python能否调用相关操作。
py::class_<std::vector<int>>(m, "IntVector")
.def(py::init<>())
.def("clear", &std::vector<int>::clear)
.def("pop_back", &std::vector<int>::pop_back)
.def("__len__", [](const std::vector<int> &v) { return v.size(); })
.def("__iter__", [](std::vector<int> &v) {
return py::make_iterator(v.begin(), v.end());
}, py::keep_alive<0, 1>()) /* Keep vector alive while iterator is used */
// ....
pybind11 源码中 tests/test_opaque_types.cpp 有使用opaque完整的例子,且细节比较丰富。
代码
- cpp代码
/*
tests/test_opaque_types.cpp -- opaque types, passing void pointers
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
// #include "pybind11_tests.h"
#include <pybind11/stl.h>
#include <vector>
#include <pybind11/pybind11.h>
namespace py = pybind11;
// IMPORTANT: Disable internal pybind11 translation mechanisms for STL data structures
//
// This also deliberately doesn't use the below StringList type alias to test
// that MAKE_OPAQUE can handle a type containing a `,`. (The `std::allocator`
// bit is just the default `std::vector` allocator).
PYBIND11_MAKE_OPAQUE(std::vector<std::string, std::allocator<std::string>>);
using StringList = std::vector<std::string, std::allocator<std::string>>;
PYBIND11_MODULE(opaque_types, m) {
// test_string_list
py::class_<StringList>(m, "StringList")
.def(py::init<>())
.def("pop_back", &StringList::pop_back)
/* There are multiple versions of push_back(), etc. Select the right ones. */
.def("push_back", (void (StringList::*)(const std::string &)) &StringList::push_back)
.def("back", (std::string &(StringList::*)()) &StringList::back)
.def("__len__", [](const StringList &v) { return v.size(); })
.def("__iter__", [](StringList &v) {
return py::make_iterator(v.begin(), v.end());
}, py::keep_alive<0, 1>());
class ClassWithSTLVecProperty {
public:
StringList stringList;
};
py::class_<ClassWithSTLVecProperty>(m, "ClassWithSTLVecProperty")
.def(py::init<>())
.def_readwrite("stringList", &ClassWithSTLVecProperty::stringList);
m.def("print_opaque_list", [](const StringList &l) {
std::string ret = "Opaque list: [";
bool first = true;
for (auto entry : l) {
if (!first)
ret += ", ";
ret += entry;
first = false;
}
return ret + "]";
});
// test_pointers
}
- py test code
from opaque_types import StringList, print_opaque_list
sl = StringList()
print_opaque_list(sl)
#%%
sl_2 = StringList()
sl_2
sl_2.push_back("1")
sl_2.push_back("hello")
sl_2.push_back("today")
sl_2.push_back("now")
print_opaque_list(sl_2)
print(sl_2.__iter__() )
for item in sl_2:
print(item)
result:
print_opaque_list(sl)
Out[27]: 'Opaque list: []'
print_opaque_list(sl_2)
Out[28]: 'Opaque list: [1, hello, today, now]'
for item in sl_2:
print(item)
1
hello
today
now