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. Of course, the first step if we want it to act like a virtual function when called from our C++ code, is to make it virtual:
class world { ... virtual const char* get() const { return "hi, world"; } ... };
Then we'll need a derived class* to help us dispatch the call to Python:
struct world_callback : hello::world { // The first argument must by a PyObject* (the corresponding Python object) // The rest of the argument list should match the base class constructor world_callback(PyObject* self, int x) : world(x), // dispatch to base object m_self(self) {} // hang onto the Python object // Dispatch the call to Python const char* get() const { // Any function arguments would go at the end of the argument list // The return type is a template parameter return py::Callback<const char*>::call_method(m_self, "get"); } // Something Python can call in case there is no override of get() const char* default_get() const { return this->hello::world::get(); } private: PyObject* m_self; // A way to hold onto the Python object };
Finally, we add world_callback
to the
ExtensionClass<>
declaration in our module initialization
function:
// Create the Python type object for our extension class static py::ExtensionClass<hello::world,world_callback> world_class("world"); ...
...and when we define the function, we must tell py_cpp about the default implementation:
// Add a virtual member function world_class.def(&hello::world::get, "get", &world_callback::default_get);
Now our subclass of hello.world
behaves as expected:
>>> class my_subclass(hello.world): ... def get(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.
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
ExtensionClass<>
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. For example:
struct baz { virtual void pure(int) = 0; }; struct baz_callback { void pure(int x) { py::Callback<void>::call_method(m_self, "pure", x); } }; extern "C" DL_EXPORT(void) initfoobar() { try { py::Module foobar("foobar"); static py::ExtensionClass<baz,baz_callback> baz_class("baz"); baz_class.def(&baz::pure, "pure"); hello.add(&baz_class); } catch(...) { py::handle_exception(); // Deal with the exception for Python } }
Now in Python:
>>> from foobar import baz >>> x = baz() >>> x.pure() Traceback (innermost last): File "", line 1, in ? AttributeError: pure >>> class mumble(baz): ... def pure(self, z): pass ... >>> y = mumble() >>> y.pure() >>>
Prev: A Simple Example Using py_cpp Next: A Peek Under the Hood 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.