[C++-sig] Re: Pyste: Virtual class wrapper doesn't refcount object
David Abrahams
dave at boost-consulting.com
Fri Apr 2 15:06:37 CEST 2004
Dan Halbert <halbert at jubilee.bbn.com> writes:
> >> Note that the self object is not properly reference-counted. It seems
> >> to me it should be
> >
> >No, Pyste generates correct code for this case. See
> >http://www.boost.org/libs/python/doc/tutorial/doc/class_virtual_functions.html.
>
> OK, I see I am incorrectly blaming Pyste.
>
> >The Python class already owns the C++ class. That would create an
> >ownership cycle.
> >
> >> since it's held by the struct, and can disappear from scope in the
> >> Python code, and so would go out of existence.
>
>
> OK, below is a complete tested small BPL example of the problem I had
> before I added the borrowed and handle<> in the callback wrapper. I
> must be misunderstanding something about object ownership.
Yep.
The basic principle is:
A wrapped C++ object is always owned by the Python object that
wraps it.
Another principle you can use is:
A when a Python object x is converted to a shared_ptr<T> p, p
maintains a reference to x, not just to *p.
I'm inverting the order of your code snippets:
| ==============================================================================
|
| Now, I'll try it in Python:
|
|
| % python
| Python 2.3.3 (#1, Mar 1 2004, 12:51:08)
| [GCC 3.2.2] on linux2
| Type "help", "copyright", "credits" or "license" for more information.
|>>> import cb
|>>> class MyCallback(cb.Callback): # subclass the C++ class
| ... def do_callback(self):
| ... print "callback called"
| ...
|>>> cback = MyCallback() # create an instance & remember it
|>>> cb.register_callback(cback) # register the callback
|>>> cb.call_callback() # call the callback
| callback called # callback works!
Right; the callback's lifetime is maintained by the the module dict's
'cback' entry.
|>>> cb.register_callback(MyCallback()) # anonymous instance, short lifetime
|>>> cb.call_callback() # call the new callback instance
| Segmentation fault # BOOM!
Right; the Python object is gone at this point.
| ==============================================================================
| If I wrote
|
| register_callback(MyCPPCallback()); // BAD IDEA; Callback doesn't live long
Right. I don't know what MyCPPCallback is, but if it's a class, you've
got the same problem as above.
| in C++, I wouldn't expect that to work at all, because the MyCPPCallback()
| object doesn't live past its use as a parameter. Maybe I shouldn't
| expect it to work in Python either?
Not unless you do something to manage the lifetime of the Python object.
| I want the C++ code to take ownership of the MyCallback PyObject
Exactly.
| or more simply, just increment its reference count.
No, you don't want that. In that case it'll leak.
| Perhaps I need a policy for the passed-in MyCallback object??
I don't see how it could help.
| The Call Policies part of the tutorial seems applicable. But all the
| appropriate-sounding policies seem to apply only to return values.
| What am I doing wrong?
The solution is to get the C++ code to hang onto the Python object,
rather than just the C++ object. Here's one approach:
|
| ==============================================================================
| #include <boost/python.hpp>
| #include <boost/cstdint.hpp>
#include <boost/shared_ptr.hpp>
| using namespace boost::python;
|
| // Original C++ class
| struct Callback
| {
| virtual void do_callback() = 0;
| };
|
| // BPL-tutorial-style wrapper, same as generated by Pyste
| struct Callback_Wrapper : Callback
| {
| Callback_Wrapper(PyObject* self_) : Callback(), self(self_) {}
| void do_callback() { call_method< void >(self, "do_callback"); }
| PyObject* self;
| };
typedef boost::shared_ptr<Callback> CallbackPtr;
| // Callback* registered_callback;
CallbackPtr registered_callback;
| // void register_callback(Callback* cb)
void register_callback(CallbackPtr cb)
| {
| registered_callback = cb;
| }
|
| void call_callback()
| {
| registered_callback->do_callback();
| }
|
| BOOST_PYTHON_MODULE(cb)
| {
| class_<Callback, boost::noncopyable, Callback_Wrapper>("Callback", init< >())
| .def("do_callback", pure_virtual(&Callback::do_callback));
|
| def("register_callback", register_callback);
| def("call_callback", call_callback);
| }
| ==============================================================================
HTH,
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
More information about the Cplusplus-sig
mailing list