OOF2: The Manual
Name
PythonExportable and PythonNative — C++ base classes for objects exported to Python
Synopsis
#include "common/pythonexportable.h template <class TYPE> class PythonExportable; template <class TYPE> class PythonNative;
Overview
The PythonExportable base class is used to
convert base class pointers exported to Python via SWIG into
derived class Python objects.
Suppose you have a C++ class hierarchy with a base class
MyExportable and derived classes
Wine and Cheese,
all of which are swigged. If you have a function
MyExportable* f() that returns a pointer to an
instance of Wine or
Cheese, the SWIG generated Python code
won't give you access to the derived parts of
Wine and Cheese.
The swigged f() will return a Python
MyExportable. This is consistent with
the way C++ does things — using a
MyExportable* only gives you access to
those parts of Wine and
Cheese that are declared in the base
class — but it's inconsistent with Python's approach. In
Python there's no such thing as a base class pointer. All
components of all objects are always accessible, if you know
they're there. (For example, you might have created a SWIGged
derived class object in Python, stored in a C++ list of base
class objects, and then retrieved it from that list in Python.
You'd expect to have all of the derived class functions
available.)
The PythonExportable and
PythonNative classes remedy this
inconsistency, allowing C++ functions to have polymorphic Python
return types.
PythonExportable
To use PythonExportable, you need to do
two things:
-
Derive a C++ class hierarchy from the templated base class
PythonExportable. The template parameter must be the name of the class derived fromPythonExportable. Each derived type must supply aclassname()virtual function that returns the name of the derived class.[92]For example:
class MyExportable : public PythonExportable<MyExportable> { }; class Wine : public MyExportable { public: virtual const std::string& classname() const { static const std::string name("Wine"); return name; } } class Cheese : public MyExportable { public: virtual const std::string& classname() const { static const std::string name("Cheese"); return name; } } -
In every swig file containing a function that returns a base class pointer (
MyExportable*in these examples), include the line:PYTHONEXPORTABLE(MyExportable);
PYTHONEXPORTABLEis defined inSRC/common/typemaps.swg. It defines a swig typemap for the base class pointer that ensures that a derived class object is returned for any swigged function that returns a base class pointer. The function would appear in the swig file like:MyExportable* f1();
PYTHONEXPORTABLEalso defines a typemap for the base class name withNewprepended. A function declared in the swig file like:NewMyExportable* f2();
will also return a derived class object, but Python will take ownership of it, deleting it when there are no more references to it.
Now when f1() or f2() is called from
Python, the returned value will be a Python instance of
Wine or Cheese, and
not a generic MyExportable.
PythonNative
If the C++ class hierarchy derived from
PythonExportable is swigged, and the
swigged Python classes are further extended by Python
inheritance, then the above mechanism doesn't quite work. The
class which is to be used as a base class for the Python
inheritance must be derived virtually from
PythonExportable. The derived classes
must also be derived from PythonNative.
PythonNative is a template, and must have
the same template parameter as
PythonExportable. The C++ constructor
must have a PyObject* argument and pass
it to the PythonNative constructor.
For example, if Cheese is swigged and
extended in Python, then it needs to have been defined like this
in C++:
class MyExportable : virtual public PythonExportable<MyExportable> { }; class Cheese : public MyExportable, public PythonNative<MyExportable> { public: Cheese(PyObject* self) : PythonNative(self) { ... } const std::string& classname(); }
and a Python constructor for the derived class that also passes the pointer:
class Cheddar(Cheese):
def __init__(self):
[cheddar specific stuff]
Cheese.__init__(self, self)
The second self in the line above is the
PyObject* self in the C++
Cheese constructor.[93]
classname() needs to be defined in the
derived C++ class, but if only the Python classes derived from
the C++ class are used, classname()
will never be called.



