c++boost.gif (8819 bytes)

Overridable Virtual Functions

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.

Pure Virtual Functions

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.