[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