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

BPL*_*BPL 10 c++ python ctypes wrapper pybind11

考虑这个小 pybind11 包装器 + 测试:

安装程序.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},
)
Run Code Online (Sandbox Code Playgroud)

包装器.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);
}
Run Code Online (Sandbox Code Playgroud)

测试.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()
Run Code Online (Sandbox Code Playgroud)

问题

现在因为我没有对顶点和索引做任何特殊的事情,所以__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)
Run Code Online (Sandbox Code Playgroud)

我的最终目标是能够在 C++ 端生成非常复杂的大量数据(将实时修改的大量数据)。因此,包装器的开销很小(理想情况下没有任何不必要的复制到 python 数据结构,只需提供原始指针)。

参考

https://pybind11.readthedocs.io/en/stable/advanced/functions.html#return-value-policies

jlu*_*ier 4

如果我没记错的话,您应该能够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);
}
Run Code Online (Sandbox Code Playgroud)

使用的构造函数py::array_t这里。如果您正在寻找类似于 C++ 中的 numpy 的东西,我还会向您指出xtensor 。