Exposing class hierarchies

This tutorial shows how you can expose a complete C++ class hierarchy as an equivalent Python class hierarchy. If desireable the Python class hierarchy can skip intermediate classes in the C++ inheritance hierarchy. In the spirit of Python any objects that are exposed using lass::python are dynamically typed. If your C++ interface returns references or (shared) pointers to parent classes, during the exposure to Python, lass:python will automagically retype the object to its most specific type. In a sense this can be described as automatic down-casting.

This tutorial contains following paragraphs:

We propose a simple class hierarchy to demonstrate the concepts: Foo, Bar and FooBar. The C++ code is in no means intended to demonstrate proper class hierarchy but is intended to show in as short as possible example the power of automatic down-casting. So in general, when you have the choice of course, you don't want to juggle around with naked pointers.
class Foo
{
public:
Foo() {}
virtual ~Foo() {}
virtual int overloaded(int iA) { return iA+1;}
int fooSpecific() { return -1; }
};

class Bar : public Foo
{
public:
Bar() {}
virtual ~Bar() {}
virtual int overloaded(int iA) { return iA+2;}
int barSpecific() { return -2; }
};

class FooBarred : public Bar
{
public:
FooBarred() {}
virtual ~FooBarred() {}
virtual int overloaded(int iA) { return iA+3;}
int fooBarSpecific() { return -3; }
};

Foo* factoryObject(int iWhich)
{
if (iWhich==3)
return new FooBarred();
if (iWhich==2)
return new Bar();
return new Foo();
}

PY_SHADOW_CLASS( LASS_DLL_EXPORT, PyFoo, Foo )
PY_DECLARE_CLASS_NAME( PyFoo, "Foo" )
PY_SHADOW_DOWN_CASTERS( PyFoo )
PY_CLASS_METHOD( PyFoo, overloaded )
PY_CLASS_METHOD( PyFoo, fooSpecific )

PY_SHADOW_CLASS_DERIVED( LASS_DLL_EXPORT, PyBar, Bar, PyFoo)
PY_DECLARE_CLASS_NAME( PyBar, "Bar" )
PY_SHADOW_DOWN_CASTERS( PyBar )
PY_CLASS_METHOD( PyBar, barSpecific )

PY_SHADOW_CLASS_DERIVED( LASS_DLL_EXPORT, PyFooBarred, FooBarred, PyBar)
PY_DECLARE_CLASS_NAME( PyFooBarred, "FooBarred" )
PY_SHADOW_DOWN_CASTERS( PyFooBarred )
PY_CLASS_METHOD( PyBar, fooBarSpecific )

PY_DECLARE_MODULE( embedding )
PY_MODULE_CLASS( embedding, PyFoo, "")
PY_MODULE_CLASS( embedding, PyBar, "")
PY_MODULE_CLASS( embedding, PyFooBarred, "")
PY_MODULE_FUNCTION( embedding, factoryObject )

PY_EXTENSION_MODULE( embedding )

Declaring a derived class

PY_SHADOW_CLASS_DERIVED( LASS_DLL_EXPORT, PyBar, Bar, PyFoo)
PY_DECLARE_CLASS_NAME( PyBar, "Bar" )
The PY_SHADOW_CLASS_DERIVED macro tells lass::python that the C++ class Bar will be shadow using identifier PyBar and will inherit any properties from the Python shadow PyFoo.

Supporting automatic down-casting

PY_SHADOW_DOWN_CASTERS( PyBar )
The PY_SHADOW_DOWN_CASTERS macro creates all necessary template code so instances of Bar can be used as argument or return value in any interface that needs to be exposed to Python objects. The _DOWN part of the macro refers to the fact that when an instance of the actual C++ class behind PyFoo (in this case, this is Foo) then lass::python is allowed to down-cast to PyBar objects whenever this makes sense. The automatic down-casting renders the Python class hierarchy the exact behavior you would expect from a Python declared class hierarchy. The type of the object will be Bar (in Python that is) and all methods will be available from Bar. In comparison, in C++ you would only have access to the functions declared in Foo but of course the behaviour filled in by the concrete objects when those functions were declared virtual.