1

Here is what I have tried so far.

#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
    }

In the python Interface

import TestWrapper as tm

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

When I look at what is stored in output.d it says,

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

I have also tried to make a this array using A in python like,

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

Bu this also resutled into some error. Any help to make the original code work or any sort of workaround will be appriciated.

4
  • Is there some reason for not using std::array<A, 5> instead of C-style array? If nope, then use it along with #include "pybind11/stl.h" and safe yourself the trouble. Commented Mar 31, 2020 at 12:58
  • @pptaszni Thanks for your comment. This works for this example. In the original task however, I am working on a project where I am not suppose to change the source code. Can you suggest something where I dont have to change the struct itself. Commented Mar 31, 2020 at 13:56
  • I am actually not sure if it is even possible, at least I didn't see any such use-case in pybind11 tests (which serves as "documentation" due to terrible official docs). If it is not allowed to touch the original code, I would just write a class wrappers for them. Do you need some example? Commented Mar 31, 2020 at 15:03
  • @pptaszni Even I am searching for the same but getting nothing. Even if I convert the c style array in the origianl code, how would I be able to use the functions which takes these structures as argument and as return type. Could you help me with this. Thanks Commented Mar 31, 2020 at 15:14

2 Answers 2

1

If you can modify your structures just a bit, go for std::array because pybind11 has a support for STL containers, but not for raw arrays.

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);
}

This solution allows you to access fields a, b, c, d in the read/write mode in python, however it is not possible to modify std::array single element, you need to modify a whole array at once.

If for some reason you absolutely must keep your structures intact, you might try to wrap them in C++ classes like this:

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);
}

However, note that with class MyB it is getting more complicated, because you need to implement operations for converting your original struct A to your new class MyA every time you access the container of class MyA inside your class MyB. I would not go this way, unless absolutely necessary.

As for using your structs in pythonic functions, there is no problem, just use them like any other pythonic variable:

import xxx
var = xxx.ModB()
def foo(x):
  print(x.d)
  x.c = 69
  return x
var = foo(var)
print(var.c)
Sign up to request clarification or add additional context in comments.

1 Comment

That's really helpful. I am going to extrapolate this methodolgy and going to write a wrapper on the functions accepting objects of B, so that I can internally change the Myclass to origianl structure before passing the argument. I will post the same here.
0

With the valuable comment provided by @pptaszni. Here is what is my implemetation for the original problem.

#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
}

On the python interface, code seems like the same

import TestWrapper as tm

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

2 Comments

Hi Ankur, does this still work if the structures are defined in a header file? does not work for me.
Hi @user3116936, i think it will work if you include the header file containing the defintation of the structure. Could you please post your sample code?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.