[C++-sig] Automation of Python object retrieval
Pierre Barbier de Reuille
pierre.barbier at cirad.fr
Wed Mar 3 14:50:16 CET 2004
I have a problem with the boost::python::object contruction. I'd want to
know if there is a way to automate the retrieval of the existing Python
object when I hold a Pointer on the C++ base class and that the class is
extended in Python. Here's an example of what I do now :
================
In C++
================
#include <boost/python.hpp>
#include <boost/python/make_constructor.hpp>
#include <string>
#include <memory>
using namespace boost::python;
struct Base
{
virtual ~Base() {}
virtual std::string toStr() const
{
return "base";
}
virtual bool operator==( Base& b )
{
return toStr() == b.toStr();
}
};
struct BaseWrap : public Base
{
BaseWrap( PyObject* self_ ) : self(self_)
{
Py_INCREF(self); /* Ensure the Python object exists as least as
long as the C++ object does */
}
BaseWrap( PyObject* self_, const Base& copy) : Base(copy), self(self_)
{
Py_INCREF(self);
}
virtual ~BaseWrap()
{
Py_DECREF(self);
}
virtual std::string toStr() const
{
return call_method<std::string>(self, "toStr");
}
std::string default_toStr() const
{
return Base::toStr();
}
virtual bool operator==( Base& b )
{
return call_method<bool>(self, "__eq__", b); // Naive approach
}
bool default_eq( Base& b)
{
return Base::operator==( b );
}
PyObject *self;
};
struct Holder
{
Holder( Base* b = NULL ) : hold( b ) {}
bool operator==( Holder& other )
{
return *hold == *other.hold;
}
std::string toStr()
{
return hold->toStr();
}
Base& getHold() { return *hold; }
Base* hold;
};
Holder* makeHolder( std::auto_ptr<Base> b )
{
Holder* obj = new Holder( b.get() );
b.release();
return obj;
};
BOOST_PYTHON_MODULE(mymod)
{
class_<Base, std::auto_ptr<BaseWrap> >("Base", init<>())
.def("__eq__", &Base::operator==, &BaseWrap::default_eq)
.def("toStr", &Base::toStr, &BaseWrap::default_toStr)
;
implicitly_convertible<std::auto_ptr<BaseWrap>, std::auto_ptr<Base>
>();
class_<Holder, boost::noncopyable>( "Holder" )
.def( "__init__", make_constructor( makeHolder ) )
.def( "__eq__", &Holder::operator== )
.def( "toStr", &Holder::toStr )
.def( "getHold", &Holder::getHold, return_internal_reference<1>() )
;
}
================
In Python:
================
from mymod import *
class Derived(Base):
def __init__(self, value):
Base.__init__(self)
self._value = value
def toStr(self):
return "Derived with value %s" % self._value
def __eq__(self,other):
if isinstance(other, Derived):
return self._value == other._value
return False
h = Holder(Derived(10))
h1 = Holder(Derived(10))
h2 = Holder(Derived("10"))
h3 = Holder(Derived(11))
print h == h1 # => 0
print h == h2 # => 0
print h == h # => 0
======================
All the equality tests fails because the other in detected as a Base
object and not a Derived object !!!
But, if I replace the BaseWrap::operator== by :
virtual bool operator==( Base& b )
{
static reference_existing_object::apply<Base&>::type convert;
BaseWrap* bw = dynamic_cast<BaseWrap*>( &b );
object other;
if( bw != NULL )
{
other = object( handle<>( borrowed( bw->self ) ) );
}
else
{
other = object( handle<>( convert( b ) ) );
}
return call_method<bool>(self, "__eq__", other);
}
I get the wanted behaviour. But I wonder if there is a cleaner method to
do that ? Or at least a method to automate that. It's always the same :
take the self PyObject* from the wrapper and use it to build the the
boost::python::object.
Thanks,
Pierre
--
Pierre Barbier de Reuille
INRA - UMR Cirad/Inra/Cnrs/Univ.MontpellierII AMAP
Botanique et Bio-informatique de l'Architecture des Plantes
TA40/PSII, Boulevard de la Lironde
34398 MONTPELLIER CEDEX 5, France
tel : (33) 4 67 61 65 77 fax : (33) 4 67 61 56 68
More information about the Cplusplus-sig
mailing list