[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