Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boost::Python, static factories, and inheritance

Tags:

c++

python

boost

So I may have a rather unique use case here, but I'm thinking it should work- But it's not working correctly.

Basically, I have a class that uses a static factory method ( create ) that returns a shared_ptr to the newly created instance of the class. This class also has a virtual function that I'd like to override from python and call from C++.

Maybe my code can express the thought more clearly than my words:

#include <string>
#include <iostream>
#include <boost/python.hpp>
#include <boost/enable_shared_from_this.hpp>

using namespace boost::python;
using namespace boost;

//~ Base Class ClassA
class ClassA
    : public enable_shared_from_this<ClassA>
{
protected:
    ClassA(){}
public:

    static shared_ptr<ClassA> create(){ return shared_ptr<ClassA>( new ClassA() ); }

    virtual void quack(){ std::cout<<"quacks like a ClassA Base"<<std::endl; }
};

//~ Wrapper for ClassA
struct WrapClassA : public ClassA, wrapper<WrapClassA>
{

    static shared_ptr<WrapClassA> create(){ return shared_ptr<WrapClassA>( new WrapClassA() ); }

    void quack()
    {
        std::cout<<"quacking like a Wrapper..."<<std::endl;
        if (override f = this->get_override("quack"))
        {
            std::cout<<"... override found!"<<std::endl;
            f();
        }
        else
        {
            std::cout<<"... no override found!"<<std::endl;
            ClassA::quack();
        }
    }

    void default_quack(){ this->ClassA::quack(); }
};

//~ C++ Call Test
void quack( shared_ptr<ClassA> ptr )
{
    ptr->quack();
}

//~ Exposing
BOOST_PYTHON_MODULE(TestCase)
{
    def( "quack", &quack );

    class_<ClassA, shared_ptr<WrapClassA>, noncopyable>( "ClassA", no_init )
        .def( "__init__", make_constructor(&WrapClassA::create) )
        .def( "quack", &ClassA::quack, &WrapClassA::default_quack )
    ;
}

//~ Main
int main()
{

    PyImport_AppendInittab( "TestCase", &initTestCase );
 Py_Initialize();

    boost::python::object main_module((boost::python::handle<>(boost::python::borrowed(PyImport_AddModule("__main__")))));
    boost::python::object main_namespace = main_module.attr("__dict__");

    boost::python::object testcase_module( (boost::python::handle<>(PyImport_ImportModule("TestCase"))) );
    main_namespace["TestCase"] = testcase_module;

    FILE* test_file = fopen("test.py", "r");
    PyRun_SimpleFile(test_file, "test.py");
    fclose( test_file );


    std::cin.get();

 return 0;
}

And here's the contents of test.py:

print "Testing.."

class Derived( TestCase.ClassA ):
    def __init__( self ):
 TestCase.ClassA.__init__( self )
    def quack( self ):
 print( "Quacks like a derived class!" )


Ainst = TestCase.ClassA()
TestCase.quack( Ainst ) #Should print 'Quacks like ClassA Base'

Dinst = Derived()
TestCase.quack( Dinst ) #Should print 'Quacks like a derived class!', but doesn't!

And the output:

Testing... quacking like a Wrapper... ... no override found! quacks like a ClassA Base quacking like a Wrapper... ... no override found! quacks like a ClassA Base

So both the base and the class derived in python are acting the same. It looks like it's not finding the override for some reason. I'm not sure but this may have something to do with the create() function. Any ideas would be greatly appreciated!

EDIT:

Added pythonquack to the py script - This works as expect:

def pythonquack( Inst ):
    print Inst
    Inst.quack()

Calling it for Ainst and Dinst says 'Quacks like a Base', and 'Quacks like a Derived', as I would expect. So for some reason the overrides aren't getting passed back to C++.

like image 656
Jon Wayne Parrott Avatar asked Nov 15 '22 13:11

Jon Wayne Parrott


1 Answers

I ended up rethinking my design using intrusive_ptrs. There was a little more work to be done with the wrappers than using shared_ptr, but it worked out fairly well. Thanks to everyone for their time.

like image 141
Jon Wayne Parrott Avatar answered Dec 15 '22 00:12

Jon Wayne Parrott