In the previous example we exposed a simple C++ class in Python and showed that we could write a subclass. We even redefined one of the functions in our derived class. Now we will learn how to make the function behave virtually when called from C++.
In this example, it is assumed that world::greet()
is a virtual
member function:
class world { public: world(int); virtual ~world(); virtual std::string greet() const { return "hi, world"; } };
We'll need a derived class* to help us dispatch the call to Python. In our derived class, we need the following elements:
PyObject*
data member that holds a
reference to the corresponding Python object.
PyObject*
argument
in the data member described above.
boost::python::callback<return-type>::call_method()
to call
the Python override.
struct world_callback : world { world_callback(PyObject* self, int x) // 2 : world(x), m_self(self) {} std::string greet() const // 3 { return boost::python::callback<std::string>::call_method(m_self, "get"); } static std::string default_get(const hello::world& self) const // 4 { return self.world::greet(); } private: PyObject* m_self; // 1 };
Finally, we add world_callback
to the
class_builder<>
declaration in our module initialization
function, and when we define the function, we must tell py_cpp about the default
implementation:
// Create the Python type object for our extension class boost::python::class_builder<hello::world,world_callback> world_class(hello, "world"); // Add a virtual member function world_class.def(&world::get, "get", &world_callback::default_get);
Now our subclass of hello.world
behaves as expected:
>>> class my_subclass(hello.world): ... def greet(self): ... return 'hello, world' ... >>> hello.length(my_subclass()) 12
*You may ask, "Why do we need this derived
class? This could have been designed so that everything gets done right
inside of hello::world
." One of the goals of py_cpp is to be
minimally intrusive on an existing C++ design. In principle, it should be
possible to expose the interface for a 3rd party library without changing
it. To unintrusively hook into the virtual functions so that a Python
override may be called, we must use a derived class.
A pure virtual function with no implementation is actually a lot easier to
deal with than a virtual function with a default implementation. First of
all, you obviously don't need to supply
a default implementation. Secondly, you don't need to call
def()
on the extension_class<>
instance
for the virtual function. In fact, you wouldn't want to: if the
corresponding attribute on the Python class stays undefined, you'll get an
AttributeError
in Python when you try to call the function,
indicating that it should have been implemented. For example:
struct baz { virtual int pure(int) = 0; }; struct baz_callback { int pure(int x) { boost::python::callback<int>::call_method(m_self, "pure", x); } }; extern "C" #ifdef _WIN32 __declspec(dllexport) #endif initfoobar() { try { boost::python::module_builder foobar("foobar"); boost::python::class_builder<baz,baz_callback> baz_class("baz"); baz_class.def(&baz::pure, "pure"); } catch(...) { boost::python::handle_exception(); // Deal with the exception for Python } }
Now in Python:
>>> from foobar import baz >>> x = baz() >>> x.pure(1) Traceback (innermost last): File "<stdin>", line 1, in ? AttributeError: pure >>> class mumble(baz): ... def pure(self, x): return x + 1 ... >>> y = mumble() >>> y.pure(99) 100
This is one area where some minor intrusiveness on the wrapped library is required. Once it has been overridden, the only way to call the base class implementation of a private virtual function is to make the derived class a friend of the base class. You didn't hear it from me, but most C++ implementations will allow you to change the declaration of the base class in this limited way without breaking binary compatibility (though it will certainly break the ODR).
Next: Function Overloading Previous: A Simple Example Using py_cpp Up: Top
© Copyright David Abrahams 2000. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.
Updated: Nov 26, 2000