Given two reference-counted classes Thing and Callback, and using a
custom smart pointer ref_ptr<T>, with all the needed pointee stuff added
and using ref_ptr<..> as held type I'm getting into trouble when trying
to use a boost::python::wrapper<> derived class. See the attached files
for the complete code.

Summarized, I have:

class Referenced { /* methods ref() and unref() */ }
class Thing : public Referenced { ...}
class Callback : public Referenced { ...}

template<class T> class ref_ptr { ... }

You can set a Callback instance on Thing, and the Callback's execute()
method gets called at some point in time. The Callback class is to be
implemented in Python (although it has a default empty implementation
for execute()), so I defined:

struct CallbackWrap : Callback, bp::wrapper<Callback>
{
    void execute(Thing* t)
    {
        if (bp::override ovr = this->get_override("execute"))
            ovr(t);
        else
            Callback::execute(t);
    }

    void default_execute(Thing* t) { this->Callback::execute(t); }
};

The whole shebang is packaged up in a BP module:

BOOST_PYTHON_MODULE(doh)
{
    bp::class_<Thing, ref_ptr<Thing>, boost::noncopyable>("Thing")
        .def("update", &Thing::update)
        .def("setCallback", &Thing::setCallback)
        ;

    bp::class_<CallbackWrap, ref_ptr<CallbackWrap>,
boost::noncopyable>("Callback")  // Don't add bp::no_init, as we won't
be able to derive a class from C in that case
        .def("execute", &Callback::execute, &CallbackWrap::default_execute)
        ;
}

But when trying to run

import doh

class MyCallback(doh.Callback):
    def execute(self, t):
        print t
        return 1

m = MyCallback()

t = doh.Thing()
t.setCallback(m)
t.update()

i'm getting

Traceback (most recent call last):
  File "no_to_python_converter.py", line 12, in <module>
    t.update()
TypeError: No to_python (by-value) converter found for C++ type: Thing".

And I fail to see why...

I'm also not sure if the message really means that there is an instance
of Thing that needs to be converted to its Python equivalent, or if
there is a _pointer_ to a Thing instance that needs to be converted to
Python.
If the former, then I'm surprised these instances even show up, as the
code being wrapped doesn't pass a Thing anywhere, only a Thing*. I've
also tagged Thing as noncopyable, so I wouldn't expect Thing instances
to be passed around internally by BP by value.

It kind of feels like BP knows how to convert an ref_ptr<Thing> to a
Python object, but not how to convert a Thing.
Any help is appreciated,

Regards,
Paul
#include <cstdio>
#include <boost/python.hpp>
#include "ref_ptr.h"

namespace bp = boost::python;

class Callback;

class Thing : public Referenced
{
public:
    Thing()     { _cb = NULL; }
    void        setCallback(Callback *cb) { _cb = cb; }
    void        update();
protected:
    virtual     ~Thing() {}

    Callback*   _cb;
};

class Callback : public Referenced
{
public:
    Callback() { }
    virtual void execute(Thing* t) { }
protected:
    virtual ~Callback() {}
};

void
Thing::update()
{
    if (_cb)
        _cb->execute(this);
}

struct CallbackWrap : Callback, bp::wrapper<Callback>
{
    void execute(Thing* t)
    {
        if (bp::override ovr = this->get_override("execute"))
        {
            fprintf(stderr, "have override, calling...\n");
            ovr(t);
            fprintf(stderr, "done calling\n");
        }
        else
            Callback::execute(t);
    }

    void default_execute(Thing* t)
    {
        fprintf(stderr, "default\n");
        this->Callback::execute(t);
    }
};

BOOST_PYTHON_MODULE(doh)
{
    bp::class_<Thing, ref_ptr<Thing>, boost::noncopyable>("Thing")
        .def("update", &Thing::update)
        .def("setCallback", &Thing::setCallback)
        ;

    bp::class_<CallbackWrap, ref_ptr<CallbackWrap>, 
boost::noncopyable>("Callback")  // Don't add bp::no_init, as we won't be able 
to derive a class from C in that case
        .def("execute", &Callback::execute, &CallbackWrap::default_execute)
        ;
}

import doh

class MyCallback(doh.Callback):
    def execute(self, t):
        print t
        return 1

m = MyCallback()

t = doh.Thing()
t.setCallback(m)
t.update()
#include <boost/python.hpp>

class Referenced
{
public:

    Referenced()                    { _refCount = 0; }
    Referenced(const Referenced&)   { _refCount = 0; }

    inline Referenced& operator = (const Referenced&) { return *this; }

    inline void ref() const;
    inline void unref() const;

protected:

    virtual ~Referenced() {}
    mutable int _refCount;
};


inline void Referenced::ref() const
{
    ++_refCount;
}

inline void Referenced::unref() const
{
    bool needDelete = (--_refCount == 0);

    if (needDelete)
        delete this;
}


template<class T>
class ref_ptr
{
public:
    ref_ptr() : _ptr(0) {}
    ref_ptr(T* ptr) : _ptr(ptr) { if (_ptr) _ptr->ref(); }
    ref_ptr(const ref_ptr& rp) : _ptr(rp._ptr) { if (_ptr) _ptr->ref(); }

    ~ref_ptr() { if (_ptr) _ptr->unref();  _ptr = 0; }

    ref_ptr& operator = (const ref_ptr& rp)
    {
        if (_ptr==rp._ptr) return *this;
        T* tmp_ptr = _ptr;
        _ptr = rp._ptr;
        if (_ptr) _ptr->ref();
        // unref second to prevent any deletion of any object which might
        // be referenced by the other object. i.e rp is child of the
        // original _ptr.
        if (tmp_ptr) tmp_ptr->unref();
        return *this;
    }

    inline ref_ptr& operator = (T* ptr)
    {
        if (_ptr==ptr) return *this;
        T* tmp_ptr = _ptr;
        _ptr = ptr;
        if (_ptr) _ptr->ref();
        // unref second to prevent any deletion of any object which might
        // be referenced by the other object. i.e rp is child of the
        // original _ptr.
        if (tmp_ptr) tmp_ptr->unref();
        return *this;
    }

    T& operator*() const { return *_ptr; }
    T* operator->() const { return _ptr; }
    T* get() const { return _ptr; }

private:
    T*  _ptr;
};

template<class T> inline
T* get_pointer(const ref_ptr<T>& rp) { return rp.get(); }

namespace boost { namespace python {

    template <class T> struct pointee< ref_ptr<T> >
    {
        typedef T type;
    };

} }
_______________________________________________
Cplusplus-sig mailing list
Cplusplus-sig@python.org
http://mail.python.org/mailman/listinfo/cplusplus-sig

Reply via email to