Search code examples
c++cnumpypybind11

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


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.


Solution

  • 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()