[C++-sig] Support for virtual functions with default implementations

David Abrahams dave at boost-consulting.com
Wed Nov 27 07:57:53 CET 2002


Some of you have been struggling with a supposed inability of
Boost.Python v2 to correctly expose virtual functions with default
implementations. You can read all about the nitty-gritty details at:

http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/*checkout*/boost/boost/libs/python/doc/polymorphism.txt

The bottom line, when exposing a class B with its "callback" class
Bcb...

   struct B
   { 
      virtual std::string f() { return "B"; }
   };

   struct BWrap : B
   { 
      BWrap(PyObject* self) : m_self(self) {}

      virtual std::string f()
      { 
         return call_method<std::string>(m_self, "f");
      }

      std::string f_default()
      {
         return B::f();
      }

      PyObject* m_self;
   };

...turns out to be that sometimes, from Python, B.f() must invoke B::f
virtually (e.g. when the Python object actually holds a [smart]
pointer to some C++ class derived from B), and sometimes it must
invoke BWrap::f_default, which invokes the actual B::f defined in B.

Well, after several weeks of working on a complicated solution to this
problem, I had an Aha! moment, and realized that we already had the
capability!** The key is simply to expose your class like this:

   class_<B, BWrap      // or, e.g., shared_ptr<BWrap>
          >("B")
       .def("f", &B::f)             // dispatch function
       .def("f", &BWrap::f_default) // default implementation
       ;

That's it! Because later def() overloads get precedence, your default
implementation will be used if the Python object actually contains a
BWrap object. Otherwise, it will call B::f virtually.

Issues: 
   1. f_default MUST be a member function of BWrap, or take a BWrap&
      first argument

   2. You MUST def() f_default second.

Failure to get either of these right will result in crashes or other
runtime failures.

I have just checked in changes which allow you to supply your default
implementation in the .def() invocation:

      .def("f", &B::f, &BWrap::f_default)

This version will catch those potential errors at
compile-time. However, the other version should work fine with the
Boost 1.29.0 release.

-Dave

**Don't worry, the weeks weren't a complete waste; the code got a lot
  cleaner as I refactored things.

-- 
                       David Abrahams
   dave at boost-consulting.com * http://www.boost-consulting.com
Boost support, enhancements, training, and commercial distribution





More information about the Cplusplus-sig mailing list