[C++-sig] Inheriting from a python base class/exception translation
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
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
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