Inheriting from a python base class/exception translation

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Inheriting from a python base class/exception translation

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 <module>
    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  0x0000000000479e9f in _PyWeakref_GetWeakrefCount (head=0x30) at Objects/weakrefobject.c:16
#1  0x000000000047ca50 in PyObject_ClearWeakRefs (object=0x892960) at Objects/weakrefobject.c:898
#2  0x000000000046a2bd 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 <Python.h>
#include <boost/python.hpp>
#include <boost/format.hpp>

using namespace boost::python;

namespace ExcTest {

template <class Exc>
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 Exc, class Base>
class_<Exc,bases<Base> >
registerExc()
{
    class_<Exc,bases<Base> > exc_class(ExcTranslator<Exc>::name,init<const std::string &>());
    exc_class
        .def("__repr__",&ExcTranslator<Exc>::repr)
        ;
    ExcTranslator<Exc>::pytype = exc_class.ptr();
    register_exception_translator <Exc>(&ExcTranslator<Exc>::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<ExcType>::pytype = 0;             \
template <> const char *ExcTranslator<ExcType>::module = #ModuleName; \
template <> const char *ExcTranslator<ExcType>::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_<BaseExc> myExc_class("BaseExc",init<const std::string &>());
    myExc_class
        .def("__str__",&BaseExc::what)
        .def("__repr__",&ExcTranslator<BaseExc>::repr)
        ;
    ExcTranslator<BaseExc>::pytype = myExc_class.ptr();
    register_exception_translator <BaseExc>(&ExcTranslator<BaseExc>::translateExc);

    registerExc<ArgExc,BaseExc>();
}

testexc.py:

#!/usr/bin/env python2.6
import exctest
import traceback

print 'testing BaseExc exception creation:'
e1 = exctest.BaseExc('BaseExc from python')
assert str(e1) == 'BaseExc from python'
assert repr(e1) == "exctest.BaseExc('BaseExc from python')"
#assert isinstance(e1,RuntimeError)
#del e1
print '  pass: %s' % (repr(e1))

print 'testing ArgExc exception creation:'
e2 = exctest.ArgExc('ArgExc from python')
assert str(e2) == 'ArgExc from python'
assert repr(e2) == "exctest.ArgExc('ArgExc from python')"
#assert isinstance(e2,RuntimeError)
assert isinstance(e2,exctest.BaseExc)
#del e2
print '  pass: %s' % (repr(e2))

print 'testing raising existing BaseExc exception object:'
try:
   raise e1
except exctest.ArgExc, e:
   traceback.print_exc()
   assert False
except exctest.BaseExc, e:
   print '  pass: %s' % (repr(e))
except:
   traceback.print_exc()
   assert False
else:
   assert False

print 'testing raising existing ArgExc exception object:'
try:
   raise e2
except exctest.ArgExc, e:
   print '  pass: %s' % (repr(e))
except exctest.BaseExc, e:
   traceback.print_exc()
   assert False
except:
   traceback.print_exc()
   assert False
else:
   assert False

print 'testing BaseExc exception translation:'
try:
   exctest.testException(0)
except exctest.ArgExc, e:
   traceback.print_exc()
   assert False
except exctest.BaseExc, e:
   print '  pass: %s' % (repr(e))
except:
   traceback.print_exc()
   assert False
else:
   assert False

print 'testing ArgExc exception translation:'
try:
   exctest.testException(1)
except exctest.ArgExc, e:
   print '  pass: %s' % (repr(e))
except exctest.BaseExc, e:
   traceback.print_exc()
   assert False
except:
   traceback.print_exc()
   assert False
else:
   assert False


print 'testing BaseExc raise:'
try:
   raise exctest.BaseExc('new BaseExc from python')
except exctest.ArgExc, e:
   traceback.print_exc()
   assert False
except exctest.BaseExc, e:
   print '  pass: %s' % (repr(e))
except:
   traceback.print_exc()
   assert False
else:
   assert False

print 'testing ArgExc raise:'
try:
   raise exctest.ArgExc('new ArgExc from python')
except exctest.ArgExc, e:
   print '  pass: %s' % (repr(e))
except exctest.BaseExc, e:
   traceback.print_exc()
   assert False
except:
   traceback.print_exc()
   assert False
else:
   assert False

print "done"

_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Passing ownership to C++ through pointer

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_<Components::Component, std::auto_ptr<Components::Component> >("Component")
.def(init<>())
.def(init<bool, boost::python::optional<const int> >())
.def(init<const std::string&, int, boost::python::optional<bool, const int>  >())
.def(init<const std::string&, const std::string&, boost::python::optional<bool, const int>  >())
;

class_<Components::UseComponent, std::auto_ptr<UseComponentWrap>, bases<Components::Component> >("UseComponent")
.def(init<>())
.def(init<const std::string&,bool, bool>())
.def(init<const Components::UseComponent&>())
;
}
}

And The object I want to take control is:

BOOST_PYTHON_MODULE(ItemModule)
{
class_<Items::Item, bases<Entity> >("Item")
.def(init<Items::Item&>())
.def(init<const std::string&>())
.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


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<Components::UseComponent> 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<Components::UseComponent> 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
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Passing ownership to C++ through pointer

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" <[hidden email]> 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_<Components::Component, std::auto_ptr<Components::Component> >("Component")
.def(init<>())
.def(init<bool, boost::python::optional<const int> >())
.def(init<const std::string&, int, boost::python::optional<bool, const int>  >())
.def(init<const std::string&, const std::string&, boost::python::optional<bool, const int>  >())
;

class_<Components::UseComponent, std::auto_ptr<UseComponentWrap>, bases<Components::Component> >("UseComponent")
.def(init<>())
.def(init<const std::string&,bool, bool>())
.def(init<const Components::UseComponent&>())
;
}
}

And The object I want to take control is:

BOOST_PYTHON_MODULE(ItemModule)
{
class_<Items::Item, bases<Entity> >("Item")
.def(init<Items::Item&>())
.def(init<const std::string&>())
.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


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<Components::UseComponent> 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<Components::UseComponent> 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
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig

_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Inheriting from a python base class/exception translation

Jim Bosch-2
In reply to this post by Nick Rasmussen
On 11/07/2011 09:19 PM, Nick Rasmussen wrote:

> 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.
>

This is a tough problem, and one I think Boost.Python wasn't designed to
do, since most of it was in place before one could even derive from
Python builtins using the C-API.  It's definitely something I'd like to
fix in the future.

In the meantime, I've essentially followed the "parallel pure-Python
exception hierarchy" method, but you can do some metaprogramming tricks
to keep from having to actually build the Python hierarchy yourself.  In
other words, write some Python code that inspects your
Boost.Python-wrapped C++ exception hierarchy and calls type(...) to make
corresponding Python types that derive from Exception.  The pure-Python
exceptions can then be set as class members of the Boost.Python-wrapped
C++ exceptions, so your translate functions can do something like this:

void translateExc(const BaseExc & exc) {
     bp::object wrappedExc(exc);
     bp::object pyExcType = wrappedExc.attr("_pyExcType")
     // assume the constructor for the Python exception accepts a
     // C++ exception; useful if the C++ exception has data
     bp::object pyExc = pyExcType(wrappedExc);
     PyErr_SetObject(pyExcType.ptr(), pyExc.ptr());
}

To finish it off, you could add a from-Python converter that converts
the pure-Python exception back to the appropriate C++ exception when
it's passed to Boost.Python-wrapped functions, but that isn't always
necessary.

I don't have any code to do the metaprogramming bits on hand, but I'm
happy to help further if you have questions.

By the way, I do hope someone else has an even better idea - my approach
is more complicated than it ought to be, but at least it's all overhead
rather than something that scales with the number of exceptions you have.

Good luck!

Jim
_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Passing ownership to C++ through pointer

Jim Bosch-2
In reply to this post by Jay Riley
On 11/08/2011 01:12 AM, Jay Riley wrote:
> I'm trying to pass an object from python to C++. I want C++ to take
> control of the object.

<snip>

> 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.
>

I suspect the inheritance is getting in the way, and Boost.Python isn't
being as smart as it could be about converting auto_ptr<T> to
auto_ptr<U> when T* is convertible to U*.

You might want to try having your register function wrapper accept
auto_ptr<UseComponentWrap> (and if necessary, providing an overload that
takes auto_ptr<UseComponent>).

Before that, however, you might want to just try using "auto_ptr &"
rather than simple "auto_ptr".  It'd be unfortunate if that was
necessary, but it's a possibility.

Good luck!

Jim
_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Inheriting from a python base class/exception translation

Nick Rasmussen
In reply to this post by Jim Bosch-2
On Tue, 08 Nov 2011, Jim Bosch wrote:

> In the meantime, I've essentially followed the "parallel pure-Python
> exception hierarchy" method, but you can do some metaprogramming
> tricks to keep from having to actually build the Python hierarchy
> yourself.  In other words, write some Python code that inspects your
> Boost.Python-wrapped C++ exception hierarchy and calls type(...) to
> make corresponding Python types that derive from Exception.  The
> pure-Python exceptions can then be set as class members of the
> Boost.Python-wrapped C++ exceptions, so your translate functions can
> do something like this:
>
> void translateExc(const BaseExc & exc) {
>     bp::object wrappedExc(exc);
>     bp::object pyExcType = wrappedExc.attr("_pyExcType")
>     // assume the constructor for the Python exception accepts a
>     // C++ exception; useful if the C++ exception has data
>     bp::object pyExc = pyExcType(wrappedExc);
>     PyErr_SetObject(pyExcType.ptr(), pyExc.ptr());
> }
>
> To finish it off, you could add a from-Python converter that
> converts the pure-Python exception back to the appropriate C++
> exception when it's passed to Boost.Python-wrapped functions, but
> that isn't always necessary.
>

Thanks, I ended up implementing something along those lines (which
was actually pretty close to what the original Python/C bindings
were doing).  I've got the basic funcionality working (exception
translation, from python conversion, to python conversion), but it
still seems a bit fragile, and only supports exceptions with a
single string.

I've attached the latest iteration of the module and testcase below.
If anyone can suggest any improvements, or a way to get it working
with standard boost wrappings of the c++ types, I'd appreciate it.

-nick

exctestmodule.cpp:

#include <Python.h>
#include <boost/python.hpp>
#include <boost/format.hpp>

using namespace boost::python;

namespace ExcTest {

template <class Exc>
struct ExcTranslator
{
    static PyObject *pytype;
    static const char *module;
    static const char *name;

    // to python
    static PyObject *convert(const Exc &exc)
    {
        return incref(object(handle<>(borrowed(pytype)))(exc.what()).ptr());
    }

    static PyTypeObject *get_pytype()
    {
        return (PyTypeObject *)pytype;
    }

    // from python
    static void *convertible(PyObject *exc)
    {
        if (!PyType_IsSubtype(Py_TYPE(exc),(PyTypeObject *)pytype)) return 0;
        return exc;
    }

    static void construct(PyObject* raw_exc, converter::rvalue_from_python_stage1_data* data)
    {
        object exc(handle<>(borrowed(raw_exc)));
        std::string s = extract<std::string>(exc.attr("__str__")());
        void *storage = ((converter::rvalue_from_python_storage<Exc>*)data)->storage.bytes;
        new (storage) Exc(s);
        data->convertible = storage;
    }

    // translate exception
    static void translate(const Exc &exc)
    {
        PyErr_SetObject(pytype,convert(exc));
    }
};

template <class Exc, class Base>
void
registerExc()
{
    std::string classname = ExcTranslator<Exc>::name;
    std::string module = ExcTranslator<Exc>::module;
    std::string basename = ExcTranslator<Base>::name;
    std::string basemodule = ExcTranslator<Base>::module;

    dict tmpDict;
    tmpDict["__builtins__"] = handle<>(borrowed(PyEval_GetBuiltins()));

    std::string definition;
    if (basemodule != module)
    {
        definition += (boost::format("import %s\n") % basemodule).str();
        basename = (boost::format("%s.%s") % basemodule % basename).str();
    }
    else
    {
        // bind in the base class type into the tmp dict
        tmpDict[basename] = object(handle<>(borrowed(ExcTranslator<Base>::pytype)));
    }

    definition += (boost::format("class %s (%s):\n"
                                 "  def __init__ (self, v=''):\n"
                                 "    super(%s,self).__init__(v)\n"
                                 "  def __repr__ (self):\n"
                                 "    return \"%s.%s('%%s')\"%%(self.args[0])\n")
                   % classname % basename % classname % module % classname).str();

    handle<> tmp(PyRun_String(definition.c_str(),Py_file_input,tmpDict.ptr(),tmpDict.ptr()));
    object exc_class = tmpDict[classname];
    scope().attr(classname.c_str()) = exc_class;
    ExcTranslator<Exc>::pytype = exc_class.ptr();

    // to python
    to_python_converter<Exc,ExcTranslator<Exc>,true>();

    // from python
    converter::registry::push_back(&ExcTranslator<Exc>::convertible,
                                   &ExcTranslator<Exc>::construct,type_id<Exc>());

    // exception translation
    register_exception_translator <Exc>(&ExcTranslator<Exc>::translate);
}

// not registered, but define RuntimeError so that BaseExc can have the appropriate
// python base type
struct RuntimeError {};
template<> PyObject *ExcTranslator<RuntimeError>::pytype   = PyExc_RuntimeError;
template<> const char *ExcTranslator<RuntimeError>::name   = "RuntimeError";
template<> const char *ExcTranslator<RuntimeError>::module = "__builtin__";

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<ExcType>::pytype   = 0;           \
template <> const char *ExcTranslator<ExcType>::module = #ModuleName; \
template <> const char *ExcTranslator<ExcType>::name   = #ExcName;

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

std::string
baseExcString(const BaseExc &exc)
{
    return exc.what();
}

std::string
argExcString(const ArgExc &exc)
{
    return exc.what();
}

BaseExc
makeBaseExc(const std::string &s)
{
    return BaseExc(s);
}

ArgExc
makeArgExc(const std::string &s)
{
    return ArgExc(s);
}

} // namespace ExcTest

using namespace ExcTest;

BOOST_PYTHON_MODULE(exctest)
{
    def("testException", &testException);
    def("baseExcString", &baseExcString);
    def("argExcString", &argExcString);
    def("makeBaseExc", &makeBaseExc);
    def("makeArgExc", &makeArgExc);

    registerExc<BaseExc,RuntimeError>();
    registerExc<ArgExc,BaseExc>();
}

testexc.py:

#!/usr/bin/env python2.6
import exctest
import traceback

print 'testing BaseExc exception creation:'
e1 = exctest.BaseExc('BaseExc from python')
assert str(e1) == 'BaseExc from python'
assert repr(e1) == "exctest.BaseExc('BaseExc from python')"
#assert isinstance(e1,RuntimeError)
#del e1
print '  pass: %s' % (repr(e1))

print 'testing ArgExc exception creation:'
e2 = exctest.ArgExc('ArgExc from python')
assert str(e2) == 'ArgExc from python'
assert repr(e2) == "exctest.ArgExc('ArgExc from python')"
#assert isinstance(e2,RuntimeError)
assert isinstance(e2,exctest.BaseExc)
#del e2
print '  pass: %s' % (repr(e2))

print 'testing raising existing BaseExc exception object:'
try:
   raise e1
except exctest.ArgExc, e:
   traceback.print_exc()
   assert False
except exctest.BaseExc, e:
   print '  pass: %s' % (repr(e))
except:
   traceback.print_exc()
   assert False
else:
   assert False

print 'testing raising existing ArgExc exception object:'
try:
   raise e2
except exctest.ArgExc, e:
   print '  pass: %s' % (repr(e))
except exctest.BaseExc, e:
   traceback.print_exc()
   assert False
except:
   traceback.print_exc()
   assert False
else:
   assert False

print 'testing BaseExc exception translation:'
try:
   exctest.testException(0)
except exctest.ArgExc, e:
   traceback.print_exc()
   assert False
except exctest.BaseExc, e:
   print '  pass: %s' % (repr(e))
except:
   traceback.print_exc()
   assert False
else:
   assert False

print 'testing ArgExc exception translation:'
try:
   exctest.testException(1)
except exctest.ArgExc, e:
   print '  pass: %s' % (repr(e))
except exctest.BaseExc, e:
   traceback.print_exc()
   assert False
except:
   traceback.print_exc()
   assert False
else:
   assert False


print 'testing BaseExc raise:'
try:
   raise exctest.BaseExc('new BaseExc from python')
except exctest.ArgExc, e:
   traceback.print_exc()
   assert False
except exctest.BaseExc, e:
   print '  pass: %s' % (repr(e))
except:
   traceback.print_exc()
   assert False
else:
   assert False

print 'testing ArgExc raise:'
try:
   raise exctest.ArgExc('new ArgExc from python')
except exctest.ArgExc, e:
   print '  pass: %s' % (repr(e))
except exctest.BaseExc, e:
   traceback.print_exc()
   assert False
except:
   traceback.print_exc()
   assert False
else:
   assert False

print 'testing exception conversion:'
be = exctest.makeBaseExc('testStr')
assert (isinstance(be,exctest.BaseExc))
assert (be.__class__ == exctest.BaseExc)
ae = exctest.makeArgExc('testStr')
assert (isinstance(ae,exctest.BaseExc))
assert (isinstance(ae,exctest.ArgExc))
assert (ae.__class__ == exctest.ArgExc)
assert (exctest.baseExcString(be) == 'testStr')
assert (exctest.baseExcString(ae) == 'testStr')
assert (exctest.argExcString(ae) == 'testStr')

print "done"

_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Passing ownership to C++ through pointer

Jay Riley
In reply to this post by Jim Bosch-2
Okay I think something else is majorly wrong; my component objects are being seen at Item onjects. I'll try to keep the code I show to a minimum. 

In my python file, I have the following:

def ItemFactory(ItemNode):
    ItemName = ItemNode.attrib["name"]
    if (True):
    #if ItemName == "Elixir":
        CurrentItem = Item(ItemName)
        print ItemName
        componentNode = ItemNode.find("Components");
        if (componentNode != None):
            components = componentNode.getchildren()
            for component in components:
                if component.tag == "HealingComponent":
                    healer = UseComponent("HealUseModule", True, True)
                    print "Adding Healing Component"
                    CurrentItem.RegisterComponent("Healing Component", healer)
        ItemModule.ItemLibrary.AddItem(CurrentItem)

ItemNode is just an XML node that I parse for properties. Each Item node gets sent to this function. As per Jim's advice, RegisterComponent looks as follows:

void RegisterComp(Items::Item& item, const std::string& compName, std::auto_ptr<UseComponentWrap> comp)//, void* comp)
{
item.RegisterComponent(compName, comp.get());
comp.release();
}

Now, the components are registering. The problem is, when I try do the following:

try
{
boost::shared_ptr<Items::Item> item = Game.GetItemLibrary().GetItem("Elixir");
Components::Component* heal = item->GetComponent("Healing Component");
Components::UseComponent* healertrue = dynamic_cast<Components::UseComponent*>(heal);
if (healertrue != nullptr)
healertrue->Use(std::vector<Character::BaseCharacter*>(), std::vector<Character::BaseCharacter*>());
}
catch (boost::python::error_already_set)
{
PyErr_Print();
}

it prints out this error:

AttributeError: 'Item' object has no attribute 'Use'

this is really confusing. How could my UseComponent have become an Item?

I was testing it, and set the AddItem function as:

static std::vector<Components::Component*> test;
void ItemDatabase::AddItem(boost::shared_ptr<Item> item)
{
if (item.get() != nullptr)
{
IDToItemMap[item->GetID()] = item;
NameToItemMap[item->GetName()] = item;
auto healComp = item->GetComponent("Healing Component");
if (healComp != nullptr)
{
test.push_back(healComp);
int x = 10;
}
}
}

Here's the weird thing. While I'm adding items, I used x = 10 as a breakpoint to watch healComp's be added to test. The first healComp get's inserted correctly, and the PyObject's tp_name says UseComponent. When the second item with a healComp is being added, I used the vector to check the first healComp inserted and it somehow became an item (tp_name says Item). This is really weird. I thought it might be a scoping problem in my python code, but I can't see anything wrong with it. I'm not sure if it's relevant, but I'm including my UseComponent wrapping

//Class definition
class UseComponentWrap : public Components::UseComponent
{
public:
UseComponentWrap(PyObject* self_);
UseComponentWrap(PyObject* self_, const std::string& name, bool hasUse, bool hasBattleUse);
UseComponentWrap(PyObject* self_, const Components::UseComponent& cmp);
bool Use(std::vector<Character::BaseCharacter*>& Users, std::vector<Character::BaseCharacter*>& Targets) override;
bool UseDefault(std::vector<Character::BaseCharacter*>& Users, std::vector<Character::BaseCharacter*>& Targets);
bool BattleUse(std::vector<Battle::BattleCharacter*>& Users, std::vector<Battle::BattleCharacter*>& Targets, Battle::BattleField* field) override;
bool BattleUseDefault(std::vector<Battle::BattleCharacter*>& Users, std::vector<Battle::BattleCharacter*>& Targets, Battle::BattleField* field);
Components::UseComponent::ClonePtr Clone() const override;
Components::UseComponent::ClonePtr CloneDefault() const;
PyObject* self;


private:

};

//Python Wrapping

class_<Components::UseComponent, std::auto_ptr<UseComponentWrap>, bases<Components::Component> >("UseComponent")
.def(init<>())
.def(init<const std::string&,bool, bool>())
.def(init<const Components::UseComponent&>())
.def("BattleUse", &Components::UseComponent::BattleUse, &UseComponentWrap::BattleUseDefault)
.def("Use", &Components::UseComponent::Use, &UseComponentWrap::UseDefault)
.def("HasUse", &Components::UseComponent::HasUse)
.def("HasBattleUse", &Components::UseComponent::HasBattleUse)
.def("Clone", &Components::UseComponent::Clone, &UseComponentWrap::CloneDefault, return_value_policy<reference_existing_object>())
.def("__copy__", &generic__copy__<Components::UseComponent>)
.def("__deepcopy__", &generic__deepcopy__<Components::UseComponent>)
;

I really don't see how this can get turned into an Item, Any ideas?

Thanks for the help so far

> Date: Tue, 8 Nov 2011 11:02:23 -0500

> From: [hidden email]
> To: [hidden email]
> Subject: Re: [C++-sig] Passing ownership to C++ through pointer
>
> On 11/08/2011 01:12 AM, Jay Riley wrote:
> > I'm trying to pass an object from python to C++. I want C++ to take
> > control of the object.
>
> <snip>
>
> > 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.
> >
>
> I suspect the inheritance is getting in the way, and Boost.Python isn't
> being as smart as it could be about converting auto_ptr<T> to
> auto_ptr<U> when T* is convertible to U*.
>
> You might want to try having your register function wrapper accept
> auto_ptr<UseComponentWrap> (and if necessary, providing an overload that
> takes auto_ptr<UseComponent>).
>
> Before that, however, you might want to just try using "auto_ptr &"
> rather than simple "auto_ptr". It'd be unfortunate if that was
> necessary, but it's a possibility.
>
> Good luck!
>
> Jim
> _______________________________________________
> Cplusplus-sig mailing list
> [hidden email]
> http://mail.python.org/mailman/listinfo/cplusplus-sig

_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Passing ownership to C++ through pointer

Jim Bosch-2
I think stepping through this with a debugger is indeed the best way to
figure out what's going on.  I'm afraid there's both too little and too
much information here for me to take a good guess at what the problem is.

I will say that exposing std::vectors of raw pointers to Python seems
like a very scary thing to do, in terms of ensuring that you have the
right memory management happening.  Weird bugs can happen when things
get destroyed unexpectedly.

If the context you're running this code permits, I'd also recommend
running this through valgrind.

Jim



On 11/08/2011 08:11 PM, Jay Riley wrote:

> Okay I think something else is majorly wrong; my component objects are
> being seen at Item onjects. I'll try to keep the code I show to a minimum.
>
> In my python file, I have the following:
>
> def ItemFactory(ItemNode):
> ItemName = ItemNode.attrib["name"]
> if (True):
> #if ItemName == "Elixir":
> CurrentItem = Item(ItemName)
> print ItemName
> componentNode = ItemNode.find("Components");
> if (componentNode != None):
> components = componentNode.getchildren()
> for component in components:
> if component.tag == "HealingComponent":
> healer = UseComponent("HealUseModule", True, True)
> print "Adding Healing Component"
> CurrentItem.RegisterComponent("Healing Component", healer)
> ItemModule.ItemLibrary.AddItem(CurrentItem)
>
> ItemNode is just an XML node that I parse for properties. Each Item node
> gets sent to this function. As per Jim's advice, RegisterComponent looks
> as follows:
>
> void RegisterComp(Items::Item& item, const std::string& compName,
> std::auto_ptr<UseComponentWrap> comp)//, void* comp)
> {
> item.RegisterComponent(compName, comp.get());
> comp.release();
> }
>
> Now, the components are registering. The problem is, when I try do the
> following:
>
> try
> {
> boost::shared_ptr<Items::Item> item =
> Game.GetItemLibrary().GetItem("Elixir");
> Components::Component* heal = item->GetComponent("Healing Component");
> Components::UseComponent* healertrue =
> dynamic_cast<Components::UseComponent*>(heal);
> if (healertrue != nullptr)
> healertrue->Use(std::vector<Character::BaseCharacter*>(),
> std::vector<Character::BaseCharacter*>());
> }
> catch (boost::python::error_already_set)
> {
> PyErr_Print();
> }
>
> it prints out this error:
>
> AttributeError: 'Item' object has no attribute 'Use'
>
> this is really confusing. How could my UseComponent have become an Item?
>
> I was testing it, and set the AddItem function as:
>
> static std::vector<Components::Component*> test;
> void ItemDatabase::AddItem(boost::shared_ptr<Item> item)
> {
> if (item.get() != nullptr)
> {
> IDToItemMap[item->GetID()] = item;
> NameToItemMap[item->GetName()] = item;
> auto healComp = item->GetComponent("Healing Component");
> if (healComp != nullptr)
> {
> test.push_back(healComp);
> int x = 10;
> }
> }
> }
>
> Here's the weird thing. While I'm adding items, I used x = 10 as a
> breakpoint to watch healComp's be added to test. The first healComp
> get's inserted correctly, and the PyObject's tp_name says UseComponent.
> When the second item with a healComp is being added, I used the vector
> to check the first healComp inserted and it somehow became an item
> (tp_name says Item). This is really weird. I thought it might be a
> scoping problem in my python code, but I can't see anything wrong with
> it. I'm not sure if it's relevant, but I'm including my UseComponent
> wrapping
>
> //Class definition
> class UseComponentWrap : public Components::UseComponent
> {
> public:
> UseComponentWrap(PyObject* self_);
> UseComponentWrap(PyObject* self_, const std::string& name, bool hasUse,
> bool hasBattleUse);
> UseComponentWrap(PyObject* self_, const Components::UseComponent& cmp);
> bool Use(std::vector<Character::BaseCharacter*>& Users,
> std::vector<Character::BaseCharacter*>& Targets) override;
> bool UseDefault(std::vector<Character::BaseCharacter*>& Users,
> std::vector<Character::BaseCharacter*>& Targets);
> bool BattleUse(std::vector<Battle::BattleCharacter*>& Users,
> std::vector<Battle::BattleCharacter*>& Targets, Battle::BattleField*
> field) override;
> bool BattleUseDefault(std::vector<Battle::BattleCharacter*>& Users,
> std::vector<Battle::BattleCharacter*>& Targets, Battle::BattleField* field);
> Components::UseComponent::ClonePtr Clone() const override;
> Components::UseComponent::ClonePtr CloneDefault() const;
> PyObject* self;
>
>
> private:
>
> };
>
> //Python Wrapping
>
> class_<Components::UseComponent, std::auto_ptr<UseComponentWrap>,
> bases<Components::Component> >("UseComponent")
> .def(init<>())
> .def(init<const std::string&,bool, bool>())
> .def(init<const Components::UseComponent&>())
> .def("BattleUse", &Components::UseComponent::BattleUse,
> &UseComponentWrap::BattleUseDefault)
> .def("Use", &Components::UseComponent::Use, &UseComponentWrap::UseDefault)
> .def("HasUse", &Components::UseComponent::HasUse)
> .def("HasBattleUse", &Components::UseComponent::HasBattleUse)
> .def("Clone", &Components::UseComponent::Clone,
> &UseComponentWrap::CloneDefault,
> return_value_policy<reference_existing_object>())
> .def("__copy__", &generic__copy__<Components::UseComponent>)
> .def("__deepcopy__", &generic__deepcopy__<Components::UseComponent>)
> ;
>
> I really don't see how this can get turned into an Item, Any ideas?
>
> Thanks for the help so far
>
>  > Date: Tue, 8 Nov 2011 11:02:23 -0500
>  > From: [hidden email]
>  > To: [hidden email]
>  > Subject: Re: [C++-sig] Passing ownership to C++ through pointer
>  >
>  > On 11/08/2011 01:12 AM, Jay Riley wrote:
>  > > I'm trying to pass an object from python to C++. I want C++ to take
>  > > control of the object.
>  >
>  > <snip>
>  >
>  > > 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.
>  > >
>  >
>  > I suspect the inheritance is getting in the way, and Boost.Python isn't
>  > being as smart as it could be about converting auto_ptr<T> to
>  > auto_ptr<U> when T* is convertible to U*.
>  >
>  > You might want to try having your register function wrapper accept
>  > auto_ptr<UseComponentWrap> (and if necessary, providing an overload that
>  > takes auto_ptr<UseComponent>).
>  >
>  > Before that, however, you might want to just try using "auto_ptr &"
>  > rather than simple "auto_ptr". It'd be unfortunate if that was
>  > necessary, but it's a possibility.
>  >
>  > Good luck!
>  >
>  > Jim
>  > _______________________________________________
>  > Cplusplus-sig mailing list
>  > [hidden email]
>  > http://mail.python.org/mailman/listinfo/cplusplus-sig
>
>
> _______________________________________________
> Cplusplus-sig mailing list
> [hidden email]
> http://mail.python.org/mailman/listinfo/cplusplus-sig

_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig