Class Virtual Functions

| Class Virtual Functions

In this section, we shall learn how to make functions behave polymorphically through virtual functions. Continuing our example, let us add a virtual function to our `Base` class:

`

        struct Base
        {
            virtual int f() = 0;
        };

`

Since `f` is a pure virtual function, `Base` is now an abstract class. Given an instance of our class, the free function `call_f` calls some implementation of this virtual function in a concrete derived class:

`

        int call_f(Base& b) { return b.f(); }

`

To allow this function to be implemented in a Python derived class, we need to create a class wrapper:

`

        struct BaseWrap : Base
        {
            BaseWrap(PyObject* self_)
                : self(self_) {}
            int f() { return call_method<int>(self, "f"); }
            PyObject* self;
        };

`

member function and methods Python, like many object oriented languages uses the term methods. Methods correspond roughly to C++'s member functions

Our class wrapper `BaseWrap` is derived from `Base`. Its overridden virtual member function `f` in effect calls the corresponding method of the Python object `self`, which is a pointer back to the Python `Base` object holding our `BaseWrap` instance.

Why do we need BaseWrap? You may ask, "Why do we need the `BaseWrap` derived class? This could have been designed so that everything gets done right inside of Base." One of the goals of Boost.Python 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. Note however that you don't need to do this to get methods overridden in Python to behave virtually when called from Python. The only time you need to do the `BaseWrap` dance is when you have a virtual function that's going to be overridden in Python and called polymorphically from C++.

Wrapping `Base` and the free function `call_f`:

`

        class_<Base, BaseWrap, boost::noncopyable>("Base", no_init)
            ;
        def("call_f", call_f);

`

Notice that we parameterized the `class_` template with `BaseWrap` as the second parameter. What is `noncopyable`? Without it, the library will try to create code for converting Base return values of wrapped functions to Python. To do that, it needs Base's copy constructor... which isn't available, since Base is an abstract class.

In Python, let us try to instantiate our `Base` class:

`

        >>> base = Base()
        RuntimeError: This class cannot be instantiated from Python

`

Why is it an error? `Base` is an abstract class. As such it is advisable to define the Python wrapper with `no_init` as we have done above. Doing so will disallow abstract base classes such as `Base` to be instantiated.

Copyright © 2002-2003 David Abrahams
Copyright © 2002-2003 Joel de Guzman
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.