[C++-sig] Inheriting from a python base class/exception translation

2011-11-07 Thread Nick Rasmussen
I'm cleaning up some of our python bindings for public release as
part of the openexr distribution, and wanted to update the python
bindings of our exception library, Iex, to use boost::python.

The first thing I did was the trivial binding of the class hierarchy
out to python, and performed register_exception_translator<> for each
type with a translator like:

void
translateExc(const BaseExc &exc)
{
PyErr_SetObject(pytype,boost::python::object(exc).ptr());
}

where pytype is the pointer to the type object for BaseExc's
python binding.  This allows instances of the exception types
to be raised in c++, translated into python, and caught.

However, the problem I'm having is this: to allow instances of
the exception types to be raised in python (or in fact to have
a try/except/else block finalize properly), the python objects
need to be derived from the python Exception type or one of
its subclasses.

I saw this thread from several years ago:

http://mail.python.org/pipermail/cplusplus-sig/2006-April/010320.html

but, at least with python 2.6, objects raised from within
a script must derive from Exception or else a TypeError will
be raised:

testing raising existing BaseExc exception object:
Traceback (most recent call last):
  File "testexc.py", line 24, in 
raise e1
TypeError: exceptions must be classes or instances, not BaseExc

So my question is this: how do I bind a c++ type out to
boost::python such that the python bindings for the type
inherits from a type defined in python?

What I've tried so far was to reflect RuntimeError out to c++
in a manner similar to how the other python builtin object
types (e.g. list/dict) are handled in boost::python.  The
reflection was successful, and I was getting correct translation
of RuntimeErrors across the python/C boundary (both for raising
and passing instances of RuntimeError objects).  I didn't
particularly expect this to work because of the object layouts, but
at least by using the c++ RuntimeError type as the parameter to
bases<>, isinstance identified the instances as being derived from
RuntimeError, and the instances were raiseable.  However, whenever
the objects were deallocated, the deallocation would crash:

#0  0x00479e9f in _PyWeakref_GetWeakrefCount (head=0x30) at 
Objects/weakrefobject.c:16
#1  0x0047ca50 in PyObject_ClearWeakRefs (object=0x892960) at 
Objects/weakrefobject.c:898
#2  0x0046a2bd in subtype_dealloc (self=0x892960) at 
Objects/typeobject.c:968

Looking in valgrind, there were unsurprisingly plenty of other
errors before the crash as well. :)

One other approach that seemed like it may work would be to define the
entire exception hierarchy in python, mirroring the c++ hierarchy,
and reflect each class individually to c++.  That doesn't seem
particularly boost-pythonic, so I'm hoping that there's a better way
such that I can just add a small fix-up in the base of the bindings
for the exception hierarchy (perhaps a custom PyTypeObject?).

Any ideas?  I've included a condensed example and testcase below:

-nick

exctestmodule.cpp:

#include 
#include 
#include 

using namespace boost::python;

namespace ExcTest {

template 
struct ExcTranslator
{
static PyObject *pytype;
static const char *module;
static const char *name;

static void translateExc(const Exc &exc)
{
PyErr_SetObject(pytype,object(exc).ptr());
}

static std::string
repr(const Exc &exc)
{
return (boost::format("%s.%s('%s')")%module%name%exc.what()).str();
}
};

template 
class_ >
registerExc()
{
class_ > exc_class(ExcTranslator::name,init());
exc_class
.def("__repr__",&ExcTranslator::repr)
;
ExcTranslator::pytype = exc_class.ptr();
register_exception_translator (&ExcTranslator::translateExc);
return exc_class;
}

struct BaseExc : public std::exception
{
explicit BaseExc(const std::string &message) : _message(message) {}
virtual ~BaseExc() throw() {}
virtual const char *what() const throw() { return _message.c_str(); }
std::string _message;
};

struct ArgExc : public BaseExc
{
explicit ArgExc(const std::string &message) : BaseExc(message) {}
virtual ~ArgExc() throw() {}
};

void
testException (int idx)
{
if (idx == 1)
throw ArgExc("ArgExc from c++");
throw BaseExc("BaseExc from c++");
}

#define PY_DEFINE_EXC(ExcType,ModuleName,ExcName) \
template <> PyObject *ExcTranslator::pytype = 0; \
template <> const char *ExcTranslator::module = #ModuleName; \
template <> const char *ExcTranslator::name = #ExcName;

PY_DEFINE_EXC(BaseExc,exctest,BaseExc)
PY_DEFINE_EXC(ArgExc,exctest,ArgExc)


} // namespace ExcTest

using namespace ExcTest;

BOOST_PYTHON_MODULE(exctest)
{
def("testException", &testException);

class_ myExc_class("BaseExc",init());
myExc_class
.def("__str__",&BaseExc::what)
.def("__repr__",&ExcTranslator::repr)
;
ExcTranslator::pytype = myExc

[C++-sig] Passing ownership to C++ through pointer

2011-11-07 Thread Jay Riley

I'm trying to pass an object from python to C++. I want C++ to take control of 
the object.
The object I'm trying to change ownership on is defined in it's module as 
follows (superfluous code removed):

BOOST_PYTHON_MODULE(ComponentModule){
class_ >("Component") 
  .def(init<>())  .def(init >()) .def(init  >())   
.def(init  >());
class_, bases >("UseComponent") 
   .def(init<>())  
.def(init()) 
.def(init())   ;   
}   }
And The object I want to take control is:
BOOST_PYTHON_MODULE(ItemModule) {   
class_ >("Item") 
.def(init())  .def(init())
.def("RegisterComponent",&Items::Item::RegisterComponent )  
;   }
Register Component is the function that takes control of the component. It's 
full definition is:
void Item::RegisterComponent(const std::string& indexName, 
Components::Component* component){   auto it = 
ComponentCollection.find(indexName);  if (it != 
ComponentCollection.end()){   
delete it->second;
ComponentCollection.erase(it);
ComponentCollection[indexName] = component; }
The item posses the component until destroyed/overwritten.
A sample piece of python code would be:
healer = UseComponent("HealUseModule", True, True)print "Adding Healing 
Component"CurrentItem.RegisterComponent("Healing Component", healer)
where CurrentItem is an item. The problem here is that when the component goes 
out of scope in the python file, it get's deleted adn invalidated since C++ 
hasn't taken ownership of it. I tried following the advice here
http://www.boost.org/doc/libs/1_36_0/libs/python/doc/v2/faq.html#ownership
which is why I wrapped Component/UseComponent in an auto_ptr. Additionally fro, 
this advice, I changed to RegisterComponent wrap to
.def("RegisterComponent", &RegisterComp)
where RegisterComp is 
void RegisterComp(Items::Item& item, const std::string& compName, 
std::auto_ptr comp)//, void* comp)  { 
  item.RegisterComponent(compName, comp.get());   
comp.release(); }
this fails, saying the Python argument types did not match the C++ signature. 
I'm certain it's the std::auto_ptr comp argument that 
is invalid because the call succeeds when that argument is removed but item and 
name are left.
Am I doing something wrong? base on that snippet it should work. Is there 
another way to do this? I know I should probably switch to smart_ptrs and I'm 
considering it, but Id like to be able to know how to do this, even if its just 
for future reference. Any help would be appreciated.
Thanks___
Cplusplus-sig mailing list
Cplusplus-sig@python.org
http://mail.python.org/mailman/listinfo/cplusplus-sig

Re: [C++-sig] Passing ownership to C++ through pointer

2011-11-07 Thread Jay Riley
Sorry just as a quick append, UseComponentWrap inherits from UseComponent to 
allow overriding of a few virtual methods

On 2011-11-08, at 1:13 AM, "Jay Riley"  wrote:

> I'm trying to pass an object from python to C++. I want C++ to take control 
> of the object.
> 
> The object I'm trying to change ownership on is defined in it's module as 
> follows (superfluous code removed):
> 
> 
> BOOST_PYTHON_MODULE(ComponentModule)
>   {
> 
>   class_ std::auto_ptr >("Component")
>   .def(init<>())
>   .def(init int> >())
>   .def(init boost::python::optional  >())
>   .def(init std::string&, boost::python::optional  >())
>   ;
> 
>   class_ std::auto_ptr, bases 
> >("UseComponent")
>   .def(init<>())
>   .def(init())
>   .def(init())
>   ;
>   }
>   }
> 
> And The object I want to take control is:
> 
> BOOST_PYTHON_MODULE(ItemModule)
>   {
>   
>   class_ >("Item")
>   .def(init())
>   .def(init())
>   
> .def("RegisterComponent",&Items::Item::RegisterComponent )
>   ;
>   }
> 
> Register Component is the function that takes control of the component. It's 
> full definition is:
> 
> void Item::RegisterComponent(const std::string& indexName, 
> Components::Component* component)
>   {
>   auto it = ComponentCollection.find(indexName);
>   if (it != ComponentCollection.end())
> {
>   delete it->second;
> ComponentCollection.erase(it);
>   ComponentCollection[indexName] = component;
>   }
> 
> The item posses the component until destroyed/overwritten.
> 
> A sample piece of python code would be:
> 
> healer = UseComponent("HealUseModule", True, True)
> print "Adding Healing Component"
> CurrentItem.RegisterComponent("Healing Component", healer)
> 
> where CurrentItem is an item. The problem here is that when the component 
> goes out of scope in the python file, it get's deleted adn invalidated since 
> C++ hasn't taken ownership of it. I tried following the advice here
> 
> http://www.boost.org/doc/libs/1_36_0/libs/python/doc/v2/faq.html#ownership
> 
> which is why I wrapped Component/UseComponent in an auto_ptr. Additionally 
> fro, this advice, I changed to RegisterComponent wrap to
> 
> .def("RegisterComponent", &RegisterComp)
> 
> where RegisterComp is 
> 
> void RegisterComp(Items::Item& item, const std::string& compName, 
> std::auto_ptr comp)//, void* comp)
>   {
>   item.RegisterComponent(compName, comp.get());
>   comp.release();
>   }
> 
> this fails, saying the Python argument types did not match the C++ signature. 
> I'm certain it's the std::auto_ptr comp argument 
> that is invalid because the call succeeds when that argument is removed but 
> item and name are left.
> 
> Am I doing something wrong? base on that snippet it should work. Is there 
> another way to do this? I know I should probably switch to smart_ptrs and I'm 
> considering it, but Id like to be able to know how to do this, even if its 
> just for future reference. Any help would be appreciated.
> 
> Thanks
> ___
> Cplusplus-sig mailing list
> Cplusplus-sig@python.org
> http://mail.python.org/mailman/listinfo/cplusplus-sig
___
Cplusplus-sig mailing list
Cplusplus-sig@python.org
http://mail.python.org/mailman/listinfo/cplusplus-sig