3

I have two abstract C++ classes which I expose and derive in python. I am trying to extract those derived objects in C++ and call some method of one of the classes, passing an instance of the other class to the first. The classes are defined as such:

struct A {
    std::vector<int> numbers;

    virtual void a() = 0;
};

struct B {
    virtual void b(const A& a) = 0;
};

I expose them to Python using boost::python

namespace py = boost::python;

struct A_wrapper : A, py:wrapper<A> {
    void a() {
        this->getoverride("a")();
    }
};

struct B_wrapper : B, py::wrapper<B> {
    void b(const A& a) {
        this->get_override("b")(a);
    }
};

BOOST_PYTHON_MODULE(example) {
    typedef std::vector<int> int_vec_t;

    py::class_<int_vec_t>("IntList")
        .def(py::vector_indexing_suite<int_vec_t>());

    py::class_<A_wrapper, boost::noncopyable>("A")
        .def_readwrite("numbers", &A::numbers)
        .def("a", py::pure_virtual(&A::a));

    py::class_<B_wrapper, boost::noncopyable>("B")
        .def("b", py::pure_virtual(&B::b));
}

My python classes just fill the numbers array with random numbers and B::b prints those numbers for debugging purposes.

I can import them and extract the two classes to boost::shared_ptrs without problems. Any python code in their initializer's is even called.

auto pyA = py::extract<boost::shared_ptr<A> >(/*py object containing derivation of A*/)();
auto pyB = py::extract<boost::shared_ptr<B> >(/*py object containing derivation of B*/)();

but when I try and call pyB->b(*pyA) I get the following error:

TypeError: No to_python (by-value) converter found for C++ type: A

What am I doing wrong? How can I call Python's derivation of B::b with an instance of A?

maddisoj
  • 576
  • 6
  • 16

1 Answers1

1

The issue is having to declare the abstract classes as boost::noncopyable. AS the documentation states:

boost::noncopyable [sic] Suppresses automatic registration of to_python conversions which copy T instances. Required when T has no publicly-accessible copy constructor.

So we can convert the python object to a C++ instance but not vica-versa which leads to the error in the question when we attempt to do just that.

The solution was in fact in the comments of the answer to this question.

In the wrapper class, pass the object by reference instead of by value; so the class does not have to be converted to python. This leaves the wrapper looking like so:

struct B_wrapper : B, py::wrapper<B> {
    void b(const A& a) {
        this->get_override("b")(boost:ref(a));
    }
};
Community
  • 1
  • 1
maddisoj
  • 576
  • 6
  • 16