如何使用 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 模式访问字段 a
、b
、c
、d
,但是无法修改 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()
这是我到目前为止所尝试过的方法。
#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 模式访问字段 a
、b
、c
、d
,但是无法修改 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()