Как связать c структуру с массивом другой структуры в качестве члена, используя pybind11? - PullRequest
0 голосов
/ 31 марта 2020

Вот что я пробовал до сих пор.

#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

Я также пытался создать этот массив, используя A в python, например,

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

Bu, это также приводило к некоторой ошибке. Любая помощь в создании оригинального кода или обходные пути будут оценены.

Ответы [ 2 ]

0 голосов
/ 01 апреля 2020

С ценным комментарием, предоставленным @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()
0 голосов
/ 31 марта 2020

Если вы можете немного изменить свои структуры, go для 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);
}

Это решение позволяет получить доступ поля a, b, c, d в режиме чтения / записи в python, однако изменить 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 MyA внутри вашего class MyB. Я бы не стал go таким образом, если бы это не было абсолютно необходимо.

Что касается использования ваших структур в функциях pythoni c, проблем нет, просто используйте их как любую другую переменную pythoni c:

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