Monday, September 22, 2008

Boost Python - Abstract Classes

In the previous article I covered setting up Boost Python to work in Visual Studio.

In this article were going to get into a bit more detail of describing the bindings of classes. Specifically were going to deal with abstract classes and polymorphism.

Consider the following:

struct AbstractClass {
virtual std::string speak() const = 0;
};

struct DerivedClassA : AbstractClass {
virtual std::string speak() const {return "Derived Class A";}
};

struct DerivedClassB : AbstractClass {
virtual std::string speak() const {return "Derived Class B";}
};


We have an Abstract Class and two derived class that implement the speak method. How do we model this in Boost Python such that in Python we can get a pointer to one of these objects and call speak on the proper derived class.

To do this we have to build a wrapper class:

struct WrapperClass : AbstractClass , boost::python::wrapper<AbstractClass> { 
virtual std::string speak() const {
return this->get_override("speak")();
}
};


This wrapper class extends the AbstractClass, and extends a special boost::python::wrapper. For each virtual method you wrap it and call it with this->get_override().

Lastly you need to define the bindings for Python, this is similar to the Hello World example in article one:

BOOST_PYTHON_MODULE(PyHelloWorld)
{
using namespace boost::python;
def("getDerivedClassA",&getDerivedClassA,return_value_policy< manage_new_object >());
def("getDerivedClassB",&getDerivedClassB,return_value_policy< manage_new_object >());
class_<WrapperClass, boost::noncopyable > ("DerivedClass")
.def("speak",boost::python::pure_virtual(&AbstractClass::speak))
;

}


Here we added two test methods called getDerivedClassA and getDerivedClassB these will be in the package PyHelloWorld as dictated by 'BOOST_PYTHON_MODULE(PyHelloWorld)'

Lastly we want to be able to execute python in the same process as our executable. If you followed the instructions in part 1 then you simply need to add this method:

void start( ) {        
using namespace boost::python;
Py_Initialize();
// Explicitly call the method that the BOOST_PYTHON_MODULE macro created.
// This sets up our DLL to be used by python.
initPyHelloWorld();
std::string pythonCommand;
try {
PyRun_SimpleString("import PyHelloWorld");
while(1) {
std::cout << ">>> ";
getline(std::cin,pythonCommand);
if (pythonCommand == "quit") {
break;
}else {
std::cout << PyRun_SimpleString(pythonCommand.c_str());
}
}
} catch ( error_already_set ) {
PyErr_Print();
}
Py_Finalize();
}



So now lets try it:

>>> import PyHelloWorld
0>>> a = PyHelloWorld.getDerivedClassA()
0>>> b = PyHelloWorld.getDerivedClassB()
0>>> print a.speak()
Derived Class A
0>>> print b.speak()
Derived Class B
0>>>

No comments: