代码之家  ›  专栏  ›  技术社区  ›  BPL

如何使用pybind11将原始指针转换为轻量级python数据类型?

  •  1
  • BPL  · 技术社区  · 3 年前

    考虑一下这个小小的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

    0 回复  |  直到 3 年前
        1
  •  4
  •   jlucier    3 年前

    如果我没记错的话,你应该能够使用 numpy 内存的数组视图非常便宜(如果不是几乎没有开销的话)。这里有一个完整的例子。

    #include <pybind11/pybind11.h>
    #include <pybind11/numpy.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];
    
        // these constructors are just views of the memory and will not copy the data
        // (nor take ownership of the pointer)
        // You could also make these shapes {numVerts, 2} and {numTris, 3} or whatever else is helpful to you.
        pyVertices = py::array_t<float>({numVerts * 2}, vertices);
        pyIndices = py::array_t<int>({numTris * 3}, indices);
      }
    
      ~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;
      py::array_t<float> pyVertices;
      py::array_t<int> pyIndices;
      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::pyVertices)
          .def_readonly("indices", &Mesh2d::pyIndices);
    }
    

    构造函数用于 py::array_t here . 我还想指出 xtensor 如果你正在寻找类似C++中的numpy。