如何使用 pybind11 将 c 结构与另一个结构的数组绑定为成员?

How to bind c structure with an array of another structre as a member, using pybind11?

这是我到目前为止所尝试过的方法。

#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
#include <pybind11/stl_bind.h>
#include <pybind11/embed.h>
#include <pybind11/pytypes.h>
#include <pybind11/numpy.h>
#include <array>


    struct A{
        int a;
        float b[10];
    };

    struct B{
        int c;
        A d[5];
    };


    namespace py = pybind11;


    PYBIND11_MODULE(TestWrapper, m) {

        PYBIND11_DTYPE(A, a, b);

        py::class_<A>(m, "A")
            .def(py::init<>())
            .def_readwrite("a", &A::a)
            .def_property("b", [](A &p)->pybind11::array {
                auto dtype = pybind11::dtype(pybind11::format_descriptor<float>::format());
                return pybind11::array(dtype, { 10 }, { sizeof(float) }, p.b, nullptr);
                }, [](A& p) {});


        py::class_<B>(m, "B")
            .def(py::init<>())
            .def_readwrite("c", &B::c)
            .def_property("d", [](B &p)->py:array {
            auto dtype = pybind11::dtype(py::format_descriptor<A>::format(),1);
            return pybind11::array(dtype, { 10 }, { sizeof(A) }, p.d, nullptr);
            }, [](B& p) {});

        #ifdef VERSION_INFOB
        m.attr("__version__") = VERSION_INFO;
    #else
        m.attr("__version__") = "dev";
    #endif
    }

在python界面

import TestWrapper as tm

input = tm.A()
output = tm.B()

当我查看存储在 output.d 中的内容时,它说,

data type "^T{i:a:(10)f:b:}" not understood

我也尝试过使用 python 中的 A 创建一个数组,例如,

instancelist = [ tm.A() for i in range(29)]
output.d = instancelist

但这也导致了一些错误。任何使原始代码工作的帮助或任何类型的解决方法都将被应用。

如果您可以稍微修改一下结构,请选择 std::array,因为 pybind11 支持 STL 容器,但不支持原始数组。

struct ModA{
  int a;
  std::array<float, 10> b;
};

struct ModB{
  int c;
  std::array<ModA, 5> d;
};

PYBIND11_MODULE(xxx, m) {
  py::class_<ModA>(m, "ModA")
    .def(py::init<>())
    .def_readwrite("a", &ModA::a)
    .def_readwrite("b", &ModA::b);
  py::class_<ModB>(m, "ModB")
    .def(py::init<>())
    .def_readwrite("c", &ModB::c)
    .def_readwrite("d", &ModB::d);
}

此解决方案允许您在 python 中以 read/write 模式访问字段 abcd,但是无法修改 std::array 单个元素,您需要一次修改整个数组。

如果出于某种原因您绝对必须保持结构完整,您可以尝试将它们包装在 C++ 类 中,如下所示:

struct A{
  int a;
  float b[10];
};

class MyA
{
public:
  // + maybe some constructor accepting struct A
  int getInt() { return a_.a; }
  void setInt(int n) { a_.a = n; }
  std::array<float, 10> getArr() {
    std::array<float, 10> result;
    std::memcpy(result.data(), a_.b, 10*sizeof(float));
    return result;
  }
  void setArr(const std::array<float, 10>& arr) {
    std::memcpy(a_.b, arr.data(), 10*sizeof(float));
  }
  void setArrIdx(float val, size_t idx) { a_.b[idx] = val; }

private:
  A a_;
};

PYBIND11_MODULE(xxx, m) {
  py::class_<MyA>(m, "MyA")
    .def(py::init<>())
    .def("getInt", &MyA::getInt)
    .def("setInt", &MyA::setInt)
    .def("getArr", &MyA::getArr)
    .def("setArr", &MyA::setArr)
    .def("setArrIdx", &MyA::setArrIdx);
}

但是请注意,使用 class MyB 会变得更加复杂,因为每次访问容器时都需要执行将原始 struct A 转换为新 class MyA 的操作class MyB 中的 class MyA。除非绝对必要,否则我不会走这条路。

至于在 pythonic 函数中使用您的结构,没有问题,只需像使用任何其他 pythonic 变量一样使用它们:

import xxx
var = xxx.ModB()
def foo(x):
  print(x.d)
  x.c = 69
  return x
var = foo(var)
print(var.c)

感谢@pptaszni 提供的宝贵意见。这是我对原始问题的实现。

#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
#include <pybind11/stl_bind.h>
#include <pybind11/embed.h>
#include <pybind11/pytypes.h>
#include <pybind11/numpy.h>
#include <array>
#include <pybind11/stl.h>
#include <pybind11/complex.h>
#include <pybind11/functional.h> 
#include <pybind11/chrono.h>

struct A{
    int a;
    float b[10];
};

struct B{
    int c;
    //std::array <A, 5> d;
    A d[5];
};

//function to be implemented with the original structure
bool doSomething(B* object)
{
    object->c = 2;
    for (int i = 0; i < 5; i++)
        for (int j = 0; j < 10; j++)
        {
            object->d[i].b[j] = 2;
        }
    return true;
}


//wrapper around the main function with the function wrapper
class MyclassB{

public:
    int c;
    std::array <A, 5> d;

    void doSomethingWrapper()
    {
        B b;

        doSomething(&b);

        c = b.c;

        for (int i = 0; i < 5; i++)
            for (int j = 0; j < 10; j++)
            {
                d[i].b[j] = b.d[i].b[j];
            }

    }
};

namespace py = pybind11;


PYBIND11_MODULE(TestWrapper, m) {

    PYBIND11_NUMPY_DTYPE(A, a, b);

    py::class_<A>(m, "A")
        .def(py::init<>())
        .def_readwrite("a", &A::a)
        .def_property("b", [](A &p)->pybind11::array {
            auto dtype = pybind11::dtype(pybind11::format_descriptor<float>::format());
            return pybind11::array(dtype, { 10 }, { sizeof(float) }, p.b, nullptr);
            }, [](A& p) {});

    py::class_<MyclassB>(m, "MyclassB")
        .def(py::init<>())
        .def_readwrite("c", &MyclassB::c)
        .def_readwrite("d", &MyclassB::d)
        .def("doSomethingWrapper", &MyclassB::doSomethingWrapper);


    #ifdef VERSION_INFOB
    m.attr("__version__") = VERSION_INFO;
#else
    m.attr("__version__") = "dev";
#endif
}

在python界面上,代码好像一样

import TestWrapper as tm

input = tm.A()
output = tm.MyclassB()
output.doSomethingWrapper()