[C++-sig] Manage new object return policy (was Re: new to python; old to C++)

David Abrahams dave at boostpro.com
Wed Nov 12 20:51:01 CET 2008


on Wed Nov 12 2008, Paul Melis <paul-AT-pecm.nl> wrote:

> David Abrahams wrote:
>> on Mon Nov 10 2008, Paul Melis <paul-AT-pecm.nl> wrote:
>>
>>   
>>> The FAQ entry on this doesn't really help, so perhaps I'm not using the return
> policy
>>> correctly or missing something else. The full test code (which is actually quite
>>> small) is attached.
>>>     
>>
>> Please reduce it to its absolute minimum.  Remove every single thing you
>> don't need in order to produce the result.  If the problem isn't
>> glaringly obvious to you then, I will help.
>>   
> Well, I tried reducing in several different ways. The bottom line is
> that I can't seem to produce this without using the wrapper class (or
> perhaps I missed the right combination of return policies).

This is a classic example of the FAQ entry.  See below.

> Simplest test case (see below for the rest of the files):
>
> #!/usr/bin/env python
> from boost import *
>  
> class MyCallback(Callback):
>  
>     def __init__(self):
>         Callback.__init__(self)
>  
>     def __call__(self):
>         return Value()

A new C++ Value instance is created here, managed by a Python Value
instance.  When this function returns, its caller's stack frame will own
the only reference to Value.  When this is invoked from C++ as an
override of a C++ virtual function, its caller is a function in
Boost.Python.

>  
> cb = MyCallback()
>  
> r = go(cb)
>
>
> Storing a reference to the Value instance returned in __call__ keeps the
> instance alive, so the dangling pointer is caused by the Value instance
> that is returned getting destructed. But this only happens when the
> instance is passed through go() as its return value. When calling cb
> directly the Value is not destructed (as I would expect). What I don't
> understand is why it is destructed when it *is* passed through go(),
> shouldn't the manage_new_object policy keep it alive?

I hope the other things I've written above and below answer the first
question.  As for the second one, manage_new_object says to manage a new
C++ object's lifetime, and the problem you have is that a Python object
already owns the C++ object, and that object will go away.

I might have a hunch about how to fix some of these problems
using a Python WeakPtr, but unfortunately I don't have any time right
now to work on it.  Best I can do is suggest you open a Trac ticket
referencing this thread.

> Regards,
> Paul
>
> // classes.h
>
> #ifndef CLASSES_H
> #define CLASSES_H
>
> #include <stdlib.h>
>
> class Value
> {
> public:
>     Value()                 { printf("Value::Value()\n"); }
>     Value(const Value& v)   { printf("Value::Value(const Value& v)\n"); }
>     ~Value()                { printf("Value::~Value()\n"); }
> };
>
> class Callback
> {
> public:
>     Callback()                      {}
>     Callback(const Callback& cb)    {}
>     virtual ~Callback()             {}
>     // Returns new Value instance
>     virtual Value* operator()() { return new Value(); }

This one doesn't get executed; it's been overridden by CallbackWrap.

> };
>
> Value *go(Callback *cb)
> {
>     return (*cb)();
> };
>
> #endif
>
>
> // boost.cpp
>
> // g++ -shared -fPIC -O3 -o boost.so -I/usr/include/python2.5 boost.cpp
> -lboost_python -lpython2.5
>
> #include <boost/python.hpp>
> #include "classes.h"
>
> namespace bp = boost::python;
>
> struct CallbackWrap : Callback, bp::wrapper<Callback>
> {
>     virtual Value *operator()()
>     {
>         printf("CallbackWrap::operator()\n");
>
>         if (bp::override call = this->get_override("__call__"))
>         {

A python override has been found here

>             printf("Have override for __call__, calling it\n");
>             return call();

It is invoked in the line above. To return control to you, Boost.Python
will reach into the Python Value object to pull out a pointer to the C++
Value object it manages.  However, in your case, the reference count on
the Python object is 1, and the C++ object will have been deleted by its
owner Python object by the time the library returns.

>         }
>
>         return Callback::operator()();
>     }
>
>     Value *default_call()
>     {
>         printf("CallbackWrap::default_call()\n");
>         return this->Callback::operator()();
>     }
> };
>
>
> BOOST_PYTHON_MODULE(boost)
> {
>     bp::class_< Value >("Value");
>
>     bp::class_< CallbackWrap, boost::noncopyable >("Callback")
>         .def("__call__", &Callback::operator(),
> &CallbackWrap::default_call,
> bp::return_value_policy<bp::manage_new_object>());
>
>     bp::def("go", &go, bp::return_value_policy<bp::manage_new_object>());
> }
>
>
>
> _______________________________________________
> Cplusplus-sig mailing list
> Cplusplus-sig at python.org
> http://mail.python.org/mailman/listinfo/cplusplus-sig
>

-- 
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


More information about the Cplusplus-sig mailing list