Inheriting C++ classes from Python

In this exercise we would like to demostrate the inheritance of a C++ class from Python. The base class in C++ is Base and we want to have an specialization of if in Python PyDerived

In [1]:
import ROOT
Welcome to ROOTaaS 6.05/01

To bridge between the C++ and Python virtual functions we need to define a proper C++ class that will forward the virtual function calls to the the Python object. The only trick here is that this C++ class needs to have a constructor to receive the self from the Python object for use afterwords. The implemetation is done completely in C++ with the next block.

In [2]:
%%cpp -d
#include "TObject.h"
#include "Python.h"
#include <stdexcept>
void call_python_method(PyObject* self, const char* method)
{
   // check arguments
   if ( 0 == self || 0 == method ) { throw std::runtime_error("Invalid Python object and method"); }
   // call Python
   PyObject* r = PyObject_CallMethod(self, const_cast<char*>(method), const_cast<char*>(""));
   if ( 0 == r ) { PyErr_Print(); return;}
   // release used objects
   Py_XDECREF( r ) ;
   //
   return;
}

class Base {
 public:
   Base() {}
   virtual ~Base() {}
   virtual void Foo() { cout << "base::Foo" << endl; }
   void CallFoo() { this->Foo(); }
};

class PyBase : public Base {
 public:
   PyBase(PyObject* self) : fSelf(self) {}
   virtual ~PyBase() {}
   virtual void Foo() { call_python_method(fSelf,"Foo"); }
 private:
   PyObject* fSelf;
};
Out[2]:
True

Now we define the python PyDerived class that inherits from the just declared C++ PyBase

In [3]:
class PyDerived(ROOT.PyBase):
   def __init__(self): ROOT.PyBase.__init__(self, self)
   def Foo(self): print 'Python::Foo'

Now that we have the two classes defined (Base and PyDerived) we exercise calling the Foo method.

In [4]:
d = PyDerived()
d.CallFoo()
o = ROOT.Base()
o.CallFoo()
Python::Foo
base::Foo

If we put the objects in a std::vector<Base*> we can exercise true polymorphic access from the C++ side

In [5]:
v = ROOT.vector('Base*')()
v.push_back(d)
v.push_back(o)
for i in v : i.Foo()
Python::Foo
base::Foo