pybind11
pybind11 copied to clipboard
PYBIND11_NUMPY_DTYPE for union struct member - cannot compute offset of bit field
Hi,
I want to bind an array of union, say I have:
typedef union A_t {
uint32_t full;
struct {
uint32_t x:16;
uint32_t y:16;
}f;
} A_t;
union B_t {
struct {
A_t regs[10];
} r ;
} B_t;
if I want to create a pybind11::array of that union I'd have to declare its DTYPE:
PYBIND11_NUMPY_DTYPE(A_t, full, f.x, f.y)
py::class_<B_t>(m, "B_t")
.def(py::init<>())
.def_property("regs", [](B_t &self) -> pybind11::array {
auto dtype = py::dtype(pybind11::format_descriptor<A_t>::format());
auto base = py::array(dtype, {10}, {sizeof(A_t)});
return py::array(
dtype, {10}, {sizeof(A_t)}, self.r.regs, base);
}, [](B_t& ) {})
but I'm getting error: error: cannot compute offset of bit-field 'x'.
Any suggestions on how to do that?
Interesting example. I can't weigh in on whether or not this can be done with pybind11::array, but I would expect that if it were possible, it would be very difficult to implement safely. The memory layout of bitfields are not standardized as far as I know and may be different in different compilers and architectures (endianness problems are a real pain with bitfields!) There are probably some compiler-specific pragmas that need to be set in order for it to work reliably.
The reason it is not working is because there is some code that needs to take the address of one of the bitfields for some reason or another, and that is not allowed in C++ (and probably not in C either.) I'm assuming the address of .x and .y are needed for this to work:
PYBIND11_NUMPY_DTYPE(A_t, full, f.x, f.y);
If it is possible in your case to not use bitfields, I would recommend that. For instance, why not use a uint16_t for x and y? Don't forget to force the packing correctly (I just use 1 all the time for stuff like this) using whatever pragma your compiler needs for it.
Maybe try this instead:
#pragma pack(push,1)
typedef union A_t {
uint32_t full;
struct {
uint16_t x;
uint16_t y;
}f;
} A_t;
#pragma pack(pop)
If it doesn't work for your real code (e.g. because you have non-byte aligned bitfield elements,) then I think you will need a much more sophisticated solution.
Good luck!
sadly i cannot change the union structures.
what if i "help" it? add a binding for A_t and then use it to return an array of that binded type?
py::class_<A_t>(m, "A_t")
.def(py::init<>())
.def_property("x",
[](A_t& self) -> int32_t
{
return self.f.x;
},
[](A_t& self, const int32_t value )
{
self.f.x = value;
})
.def_property("y",
[](A_t& self) -> int32_t
{
return self.f.y;
},
[](A_t& self, const int32_t value )
{
self.f.y = value;
})
;
That's definitely worth a try. Let me know how it goes.
I meant I dont know how to do that :) it was an idea but I need help with implementing..
what if i "help" it? add a binding for A_t and then use it to return an array of that binded type?
py::class_<A_t>(m, "A_t") .def(py::init<>()) .def_property("x", [](A_t& self) -> int32_t { return self.f.x; }, [](A_t& self, const int32_t value ) { self.f.x = value; }) .def_property("y", [](A_t& self) -> int32_t { return self.f.y; }, [](A_t& self, const int32_t value ) { self.f.y = value; }) ;
It work,