[C++-sig] Re: Making copy of function derived from C++ pure virtual function

David Abrahams dave at boost-consulting.com
Thu Dec 9 04:06:37 CET 2004


Paul F. Kunz wrote:
>>>>>> On Wed, 08 Dec 2004 14:44:52 -0500, David Abrahams
<dave at boost-consulting.com> said:
>
>> Paul F. Kunz wrote:
>>> I have a function written in Python derived from C++.  Now I want
>>> to clone this function in C++.  What should I do in the copy
>>> constructor of the function wrapper?  I have...
>
>> Way too much detail missing for me to say anything useful.
>
>    Sorry, I'll be more verbose.
>
>> Start by defining your terms (or used established terminology):
>
>> What does it mean to have a "function written in Python derived from
>> C++?"  What does it mean to clone a function?
>
>    Following the Boost.Python tutorial, I have a abstract C++ base
> class, FunctionBase,  with pure virtual functions.   Derived from it
> is my wrapper class, FunctionWrap, which contains the implementation
> of the pure virtual functins of the base that uses call_method<> ()
> (...) to call the Python function.   I then implement a function in
> Python that derives from FunctionBase, FunctionWrap...

Functions in Python generally don't derive from anything.  You
implement a _class_ derived from FunctionBase?

>    export_FunctionBase ()
>     {
>       class_ < FunctionBase, FunctionWrap, boost::noncopyable  >
>         ( "FunctionBase" )

Uhh, that's just the Python representation of FunctionBase, not
anything derived from FunctionBase.

>
>>> FunctionWrap:: FunctionWrap ( const FunctionWrap & wrap )
>>>  : FunctionBase ( wrap ),
>>>    m_self ( wrap.m_self )
>>>  { // what should be here to create a new PyObject that is copy of
old one?


What you probably want is something like this:

  FunctionWrap:: FunctionWrap ( PyObject* self, const FunctionBase& f )
    : FunctionBase ( f ),
      m_self ( self )
  {}

So you can write

  .def(init<FunctionBase const&>())

When wrapping FunctionBase.  This all looks a lot simpler when you are
using new-style polymorphism, though.

>> What old one?
>
> The object wrap.m_self which is pointer to PyObject in the constructor...


> FunctionWrap::
> FunctionWrap ( PyObject * self )
>   : FunctionBase (),
>     m_self ( self )
> {
> }
>
>
> The C++ code makes a copy of the function by calling virtual method
> clone(), which is implemented as ...
>
> FunctionBase *
> FunctionWrap::
> clone () const
> {
>   return new FunctionWrap ( *this );
> }

clone is a virtual function, right?  Normally you should be wrapping
it just like any other virtual function; something like:

  FunctionBase* FunctionWrap::clone() const
  {
      return call_method<FunctionBase*>(m_self, "clone");
  }

And then in Python you'd write:

  class MyFunction(FunctionBase):
     def clone(self):
         return MyFunction(self)

But you're going to have a big ownership problem if this clone() is
ever called from C++ because the new C++ FunctionWrap object will be
owned by the newly-created Python MyFunction object.  When call_method
returns the last reference to that Python object will go away and
destroy the C++ object so you'll be returning a dangling pointer.

Okay, here's what I suggest, using new-style polymorphism, untested:

  template <class T>
  object get_owner(T* me)
  {
      // Use secret interface to get the Python object
      // that owns *this.  I guess I will have to make that
      // interface public.
      return
object(handle<>(borrowed(python::detail::wrapper_base_::get_owner(this))));
  }


  class FunctionWrap : public FunctionBase, public wrapper<FunctionBase>
  {
      // expose this with def(init<FunctionBase const&>())
      FunctionWrap(FunctionBase const& other)
        : FunctionBase(other)
      {}

      FunctionBase* clone()
      {
           object py_result;

           if (override clone = this->get_override("clone"))
           {
                // The Python class author overrode clone; do
                // whatever she says
                py_result = clone();
           }
           else
           {
                object self = get_owner(this);

                // Find its most-derived Python class
                object my_class = self.attr("__class__");

                // call the copy ctor through Python.
                py_result = my_class(self);
           }
           FunctionWrap* result = extract<FunctionBase*>(py_result);

           // Make the C++ result control the destiny of the Python result.
           result->invert_ownership = py_result;
           return result;
      }

      // Make sure that destroying the Python object doesn't cause an
      // attempt to destroy this one again.
      ~FunctionBase()
      {
            extract<std::auto_ptr<FunctionWrap>&> x(get_owner(this));

            if (x)
                x().release();
      }

   private:
      object invert_ownership;
  };

  ...

  class_<std::auto_ptr<FunctionWrap> >("FunctionBase", init<FunctionBase
const&>())
       ...


I hope this makes sense (and works!)

And I hope that you'll make it into an instructive example that can
be included in the tutorial.

-- 
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com




More information about the Cplusplus-sig mailing list