Lunchbox
Lunchbox

Reputation: 2156

Boost python and virtual classes

I am working on exposing some existing C++ code to Python.

I have a virtual class:

class SomeVirtualClass {
public:
    SomeVirtualClass();
    virtual ~SomeVirtualClass();
    virtual SomeVirtualClass *clone() const = 0;
    virtual SomeVirtualClassType getType() const = 0;
    // SomeVirtualClassType is a descriptive enum
    virtual std::string getSomeString() const = 0;
private:
    //some other things here
}

and a class that makes use of the virtual type:

class A {
    public:
        SomeVirtualClass getThisThing();
    }

I need to be able to use getThisThing from A in python. The method can return one of a number of classes derived from SomeVirtualClass. Using the boost doc and several similar stackoverflow questions, I have come up with:

namespace bp = boost::python
class SomeVirtualClassWrap : public SomeVirtualClass,
                         public bp::wrapper<SomeVirtualClass> {
    public:
    std::string getSomeString() {
        return this->get_override("getSomeString")();
    }

    SomeVirtualClass *clone() {
        return this->get_override("clone")();
    }

    SomeVirtualClassType getType() {
        return this->get_override("getType")();   
    }
};

As a wrapper for the virtual class and:

bp::class_ <SomeVirtualClassWrap, boost::noncopyable>("SomeVirtualClass", no_init)
        .def("GetSomeString", bp::pure_virtual(&SomeVirtualClass::getSomeString))
        .def("Clone", bp::pure_virtual(&SomeVirtualClass::clone))
        .def("GetType", bp::pure_virtual(&SomeVirtualClass::getType));

As the python exposure.

for reference Class A is also exposed:

bp::class_<A>("A") {
    .def("GetThisThing", &A::getThisThing)
}

When I try to compile I get:

error: cannot declare field 'boost::python::objects::value_holder<SomeVirtualClassWrap>::m_held' to be of abstract type 'SomeVirtualClassWrap'
src/lib/objects/sub_mtrl/rebarmodule.c:412:7: note:   because the following virtual functions are pure within 'SomeVirtualClassWrap':
ThisIsAHeader.h:42:27: note:     virtual SomeVirtualClass* SomeVirtualClass::clone() const
ThisIsAHeader.h:43:30: note:     virtual SomeVirtualClassType SomeVirtualClass::getType() const
ThisIsAHeader.h:44:25: note:     virtual std::string SomeVirtualClass::getSomeString() const
In file included from tools/boost-for-sds2-2016/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:47:0,
                 from tools/boost-for-sds2-2016/include/boost/python/object/value_holder.hpp:50,
                 from tools/boost-for-sds2-2016/include/boost/python/object/class_metadata.hpp:11,
                 from tools/boost-for-sds2-2016/include/boost/python/class.hpp:23,
                 from tools/boost-for-sds2-2016/include/boost/python.hpp:18

I need to be able to call A.GetThisThing() from Python and handle the object. I would prefer to leave the existing code as is, if at all possible.

EDIT: As per the comment, I added no_init to the python exposure code and the error about pure methods cleared up. Now I am left with:

error: no match for call to '(const boost::python::detail::specify_a_return_value_policy_to_wrap_functions_returning<SomeVirtualClass*>) (SomeVirtualClass*)

EDIT: Removing the *clone method from the Python exposure cleared that error up. Turns out I didn't need that one anyway. Just working on a conversion error.

Upvotes: 0

Views: 360

Answers (1)

Lunchbox
Lunchbox

Reputation: 2156

As per Luka Rahne's comment, adding no_init fixed the compilation error:

bp::class_ <SomeVirtualClassWrap, boost::noncopyable>("SomeVirtualClass", no_init)
        .def("GetSomeString", bp::pure_virtual(&SomeVirtualClass::getSomeString))
        .def("Clone", bp::pure_virtual(&SomeVirtualClass::clone))
        .def("GetType", bp::pure_virtual(&SomeVirtualClass::getType));

Special shout out to n.m. as well.

Upvotes: 1

Related Questions