joe.dinius
joe.dinius

Reputation: 416

Fully-transparent exposure of Eigen::Vector/Matrix types using pybind11

I have a simple class definition:

class State {
  private:
      Eigen::Vector3f m_data;

  public:
      State(const Eigen::Vector3f& state) : m_data(state) { }
      Eigen::Vector3f get() const { return m_data; }
      void set(const Eigen::Vector3f& _state) { m_data = _state; }
      std::string repr() const {
          return "state data: [x=" + std::to_string(m_data[0]) + ", y=" + std::to_string(m_data[1]) + ", theta=" + std::to_string(m_data[2]) + "]";
      }
};

I then expose the above in python with pybind11:

namespace py = pybind11;

PYBIND11_MODULE(bound_state, m) {
    m.doc() = "python bindings for State";
    py::class_<State>(m, "State")
        .def(py::init<Eigen::Vector3f>())
        .def("get", &_State::get)
        .def("set", &_State::set)
        .def("__repr__", &_State::repr);
}

And everything works fine; I am able to import this module into python and construct a State instance with a numpy array. This isn't exactly what I want though. I want to be able to access this object as if it were a numpy array; I want to be able to do something like the following in python:

import bound_state as bs
arr = np.array([1, 2, 3])
a = bs.State(arr)
print(a[0])

(the above throws a TypeError: 'bound_state.State' object does not support indexing)

In the past, I've used boost::python to expose lists by using add_property and this allowed indexing of the underlying data in C++. does pybind11 have something similar that can work with Eigen? Could someone provide an example showing how to expose a State instance that is indexable?

Upvotes: 1

Views: 445

Answers (1)

joe.dinius
joe.dinius

Reputation: 416

Per the API Docs, this can be done easily with the def_property method.

Turn this bit:

namespace py = pybind11;

PYBIND11_MODULE(bound_state, m) {
    m.doc() = "python bindings for State";
    py::class_<State>(m, "State")
        .def(py::init<Eigen::Vector3f>())
        .def("get", &State::get)
        .def("set", &State::set)
        .def("__repr__", &State::repr);
}

Into this:

namespace py = pybind11;

PYBIND11_MODULE(bound_state, m) {
    m.doc() = "python bindings for State";
    py::class_<State>(m, "State")
        .def(py::init<Eigen::Vector3f>())
        .def_property("m_data", &State::get, &State::set)
        .def("__repr__", &State::repr);
}

Now, from the python-side, I can do:

import bound_state as bs
arr = np.array([1, 2, 3])
a = bs.State(arr)
print(a.m_data[0])

This is not exactly what I want, but is a step in the right direction.

Upvotes: 1

Related Questions