Virtual Functions with Default Implementations

Recall that in the previous section, we wrapped a class with a pure virtual function that we then implemented in C++ or Python classes derived from it. Our base class:

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

had a pure virtual function f. If, however, its member function f was not declared as pure virtual:

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

and instead had a default implementation that returns 0, as shown above, we need to add a forwarding function that calls the Base default virtual function f implementation:

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

Then, Boost.Python needs to keep track of 1) the dispatch function f and 2) the forwarding function to its default implementation default_f. There's a special def function for this purpose. Here's how it is applied to our example above:

    class_<Base, BaseWrap>("Base")
        .def("f", &Base::f, &BaseWrap::default_f)

Note that we are allowing Base objects to be instantiated this time, unlike before where we specifically defined the class_<Base> with no_init.

In Python, the results would be as expected:

    >>> base = Base()
    >>> class Derived(Base):
    ...     def f(self):
    ...         return 42
    ...
    >>> derived = Derived()

Calling base.f():

    >>> base.f()
    0

Calling derived.f():

    >>> derived.f()
    42

Calling call_f, passing in a base object:

    >>> call_f(base)
    0

Calling call_f, passing in a derived object:

    >>> call_f(derived)
    42