2

My goal is to modify array which are declared in C++ struct and assigned with default value.

I have read this, this, but unfortunately I cannot relate it with my problem.

Sample Code

  • C++
class Math{
struct Data
{
  std::array<float, 5> id_ = {0}; // all value set to ZERO
  std::array<uint32_t, 5> length_ = {0}; // all value set to ZERO
  std::array<bool, 5> status_ = {0}; // all value set to ZERO
  float x_ = 7.5;
};
};
  • Binded Code
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/complex.h>

namespace py = pybind11;

PYBIND11_MODULE(do_math, m)
{
    py::class_<Math::Data> Data (m, "Data");
    Data.def(py::init<>())
    .def_readwrite("id_", &Math::Data::id_)
    .def_readwrite("length_", &Math::Data::length_)
    .def_readwrite("status_", &Math::Data::status_)
    .def_readwrite("x_", &Math::Data::x_);
}
  • Now, I would like to modify all std::array member value. I am only showing here id_.
  • In python file I can access the id_ member variable and it prints [0.0, 0.0, 0.0, 0.0, 0.0] as well as the x_ which output is 7.5
import do_math
struct_obj = do_math.Data()
print(struct_obj.id_)
print(struct_obj.x_)
  • Now would like to modify the value of id_ but here I am unable to do it.
struct_obj.id_[2] = 2.2 # cannot modify
struct_obj.x_ = 1.5 # it is modified

Still output of struct_obj.id_ is [0.0, 0.0, 0.0, 0.0, 0.0] while struct_obj.x_ is changed to 1.5. How can I modify the id_ array in python?

Approach has taken so far

By following this answer I have tried to implement but failed.

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/complex.h>
#include "pybind11/numpy.h"
#include <pybind11/pytypes.h>

namespace py = pybind11;

PYBIND11_MODULE(do_math, m)
{
    py::class_<Math::Data> Data (m, "Data", py::buffer_protocol());
    Data.def(py::init<>())
    .def_property("id_", [](Math::Data &p) -> py::array {
            auto dtype = py::dtype(py::format_descriptor<float>::format());
            auto base = py::array(dtype, {5}, {sizeof(float)});
            return py::array(
                dtype, {5}, {sizeof(float)}, p.id_, base);
        }, [](Math::Data& p) {});
}
  • Error Message : error: no matching constructor for initialization of 'py::array' return py::array(

2 Answers 2

1

Is the type of struct_obj.id_ a pythonic list? I assume this is the problem. In such a case, a new pythonic list is created as a copy of the C++ array to offer a convenient Pythonic interface. However, this list is not connected anymore to the underlying C++ object because a list can not be a reference to a std::array.

The solution is either to assign id_ directly instead of id_[2]:

l = struct_obj.id
l[2] = 2.2 # cannot modify
struct_obj.id = l

or to export id_ as an opaque object with PYBIND11_MAKE_OPAQUE.

Sign up to request clarification or add additional context in comments.

1 Comment

I have tried to use opaque object but failed. Can you kindly show me an approach on the basis of my example?
0

As several posts I have found that to wrap std::array there is no ready made solution in pybind11 that's why I have followed this answer to use def_property & lambda expression to assign/modify std::array from Python and return the value to python from C++.

  • Binded code
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/complex.h>
#include "pybind11/numpy.h"
#include <pybind11/pytypes.h>

namespace py = pybind11;

PYBIND11_MODULE(do_math, m)
{
    py::class_<Math::Data> Data (m, "Data");
    Data.def(py::init<>())
    .def_property("id", [](Math::Data &self) {return self.id;},
                        [](Math::Data &self, std::array<float,5>buffer) 
                        {self.id = buffer;});
}
  • Then usage from Python side
import do_math
struct_obj = do_math.Data()
input_id = [1.1,2.2,3.3,7.8,0.5]
struct_obj.id = input_id # Now id can print the desired output 

Although the approach has solved my issue but I am not satisfied. Reasons are following:

  • Each time if I have to update id array, I have to change input_id. Time consuming and maintenance is hard. But it is the demand of the lambda expression which I have used
  • Implementation of the wrapper in the binded code seems error-prone to me as I have to each time declare the array (for buffer) which has already declared in cpp header/source file. If I can get a way to write the wrapper in such way which can automatically detect the array(here id) and return the updated/assigned/modified value could be best.

Looking for a better and cleaner solution.

Comments

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.