考虑一下这个小小的pybind11包装器+测试:
setup.py
from pybind11.setup_helpers import Pybind11Extension
from pybind11.setup_helpers import build_ext
from setuptools import setup
setup(
name="wrapper",
ext_modules=[Pybind11Extension("wrapper", ["wrapper.cpp"])],
cmdclass={"build_ext": build_ext},
)
wrapper.cpp
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <vector>
namespace py = pybind11;
struct Mesh2d {
public:
Mesh2d(int numVerts, int numTris)
: numVertices(numVerts), numTriangles(numTris) {
vertices = new float[numVerts * 2];
indices = new int[numTris * 3];
}
~Mesh2d() {
delete vertices;
delete indices;
}
void makeVertex(int i, float x, float y) {
vertices[i * 2 + 0] = x;
vertices[i * 2 + 1] = y;
}
void makeTriangle(int i, int a, int b, int c) {
indices[i * 3 + 0] = a;
indices[i * 3 + 1] = b;
indices[i * 3 + 2] = c;
}
float *vertices;
int *indices;
int numVertices;
int numTriangles;
};
PYBIND11_MODULE(wrapper, m) {
m.doc() = "mcve";
py::class_<Mesh2d>(m, "Mesh2d")
.def(py::init<int, int>())
.def("make_vertex", &Mesh2d::makeVertex)
.def("make_triangle", &Mesh2d::makeTriangle)
.def_readonly("vertices", &Mesh2d::vertices)
.def_readonly("indices", &Mesh2d::indices);
}
test.py
from wrapper import Mesh2d
def test():
m = Mesh2d(3, 1)
m.make_vertex(0, -1.0, -1.0)
m.make_vertex(1, 1.0, -1.0)
m.make_vertex(2, 0.0, 1.0)
m.make_triangle(0, 0, 1, 2)
print(m.vertices.__class__)
print(m.indices.__class__)
test()
提问
现在,因为我对顶点和索引都没有做任何特别的事情
__class__
我即将进入python世界
<class 'float'>
和
<class 'int'>
分别。将这些原始指针转换为合适对象的正确pybind11方法是什么,例如:
array.array("f", vertices)
array.array("I", indices)
(ctypes.c_float * 6)(*vertices)
(ctypes.c_uint * 3)(*indices)
memoryview(struct.pack("6f", *vertices))
memoryview(struct.pack("3I", *indices))
struct.pack("6f", *vertices)
struct.pack("3I", *indices)
我在这里的最终目标是能够在c++端生成非常复杂的重数据(将实时修改的重数据)。因此,包装器上的开销很小(理想情况下,不需要对python数据结构进行任何不必要的复制,只需要提供原始指针)。
参考资料
https://pybind11.readthedocs.io/en/stable/advanced/functions.html#return-value-policies