[C++-sig] Re: Dangling Reference Exception dramas
Daniel Paull
dlp at fractaltechnologies.com
Thu May 22 03:43:08 CEST 2003
Hi Dave,
I've formulated a test case which shows up the problem. See the listing
at the end of this post. Here is what is happening:
1) There is a class Foo which is being exposed to Python with the
intention of being subclassed.
2) A subclass of Foo is passed from Python to C++ via the setFoo()
method. In my real case this is registry of objects, not just a global
pointer.
3) The getBar() method is called on the Foo returned in (2)
The code as listed below produces the following output:
Foo()
Got the Foo object
hello: <__main__.MyFoo object at 0x0032DDC8>
ReferenceError: Attempt to return dangling pointer to object of
type: class Bar
~Foo()
If you uncomment the line "self.bar_extra_ref = self.bar" in MyFoo's
__init__ method, you get the following output:
Foo()
Got the Foo object
hello: <__main__.MyFoo object at 0x009426F0>
~Foo()
This shows that introducing one extra reference to the Bar object stops
the exception from being signalled, hence, the reference count of
self.bar must be 2 in the original listing (as getBar() returns). It
was this observation that made me think that the "<= 2" check noted in
my original post may be in error.
---------------------------------------
#include <iostream.h>
#include <boost/python.hpp>
using namespace boost::python;
class Bar {};
class Foo
{
public:
Foo(){ cout << "Foo()" << endl; }
virtual ~Foo() { cout << "~Foo()" << endl; }
virtual Bar *getBar() { return 0; }
virtual void dump() {}
};
class FooWrapper : public Foo
{
public:
FooWrapper( PyObject* pSelf ) : m_pSelf( pSelf ) {}
virtual ~FooWrapper() {}
virtual Bar *getBar()
{
return call_method<Bar*>( m_pSelf, "getBar" );
}
virtual void dump()
{
call_method<void>( m_pSelf, "dump" );
}
PyObject* m_pSelf;
};
// global pointer kludge
static Foo* g_pFoo = 0;
void setFoo( Foo* pFoo ) { g_pFoo = pFoo; }
BOOST_PYTHON_MODULE(hello)
{
class_< Bar >( "Bar" );
class_< Foo, FooWrapper, boost::noncopyable >( "Foo" )
.def( "getBar", &FooWrapper::getBar,
return_internal_reference<>() )
.def( "dump", &FooWrapper::dump )
;
def( "setFoo", &setFoo );
}
void main( void )
{
Py_Initialize();
PyImport_AppendInittab("hello", inithello);
// here we create a subclass of Foo in Python and
// pass it back to C++ via the setFoo() method
handle<> main_module(borrowed( PyImport_AddModule("__main__")
));
handle<> main_namespace(borrowed(
PyModule_GetDict(main_module.get()) ));
handle<>( PyRun_String(
"import hello\n"
"\n"
"class MyFoo( hello.Foo ):\n"
" def __init__( self ):\n"
" hello.Foo.__init__( self )\n"
" self.bar = hello.Bar()\n"
" #self.bar_extra_ref = self.bar"
"\n"
" def getBar( self ):\n"
" return self.bar\n"
"\n"
" def dump( self ):\n"
" print 'hello: ' + str(self)\n"
"\n"
"foo = MyFoo()\n"
"hello.setFoo( foo )\n",
Py_file_input, main_namespace.get(),
main_namespace.get()) );
// now we extract the Bar object from g_foo
if ( g_pFoo )
{
cout << "Got the Foo object" << endl;
try
{
g_pFoo->dump();
Bar *pBar = g_pFoo->getBar();
}
catch( error_already_set )
{
PyErr_Print();
}
}
Py_Finalize();
}
---------------------------------------
> -----Original Message-----
> From: c++-sig-admin at python.org [mailto:c++-sig-admin at python.org] On
Behalf
> Of David Abrahams
> Sent: Wednesday, 21 May 2003 10:39 PM
> To: c++-sig at python.org
> Subject: [C++-sig] Re: Dangling Reference Exception dramas
>
> "Daniel Paull" <dlp at fractaltechnologies.com> writes:
>
> > Hello,
> >
> > I've hit a situation somewhat similar to that mentioned in the, "I'm
> > getting the "attempt to return dangling reference" error. What am I
> > doing wrong?", section of the FAQ.
> >
> > The FAQ describes a situation like this:
> >
> > period const& get_floating_frequency() const
> > {
> > return boost::python::call_method<period const&>(
> > m_self,"get_floating_frequency");
> > }
> >
> > class MyClass( ... ):
> > def get_floating_frequency( self ):
> > return period( 25 )
> >
> > Clearly, the period C++ object will be destroyed as the returned
python
> > object is garbage collected, as noted in the FAQ. However, the
object
> > being returned in my case is an attribute of the class. For
example:
> >
> > class MyClass( ... ):
> > def __init__( self ):
> > self.period = period( 25 )
> >
> > def get_floating_frequency( self ):
> > return self.period
> >
> > By my reckoning this should be safe (so long as I manage the
lifetime of
> > the class instance properly). However, I still get a
ReferenceException
> > raised.
>
> What is the precise code which causes the exception? Can you post a
> reduced test case?
>
> > Looking at the boost.python code I see (in from_python.cpp):
> >
> >
----------------------------------------------------------------------
> > void* lvalue_result_from_python(
> > PyObject* source
> > , registration const& converters
> > , char const* ref_type)
> > {
> > handle<> holder(source);
> > if (source->ob_refcnt <= 2)
> > {
> > handle<> msg(
> > ::PyString_FromFormat(
> > "Attempt to return dangling %s to object of type:
%s"
> > , ref_type
> > , converters.target_type.name()));
> >
> > PyErr_SetObject(PyExc_ReferenceError, msg.get());
> >
> > throw_error_already_set();
> > }
> >
> > void* result = get_lvalue_from_python(source, converters);
> > if (!result)
> > (throw_no_lvalue_from_python)(source, converters,
ref_type);
> > return result;
> > }
> > }
> >
----------------------------------------------------------------------
> >
> > I'm wondering if the "<= 2" test should be "< 2". Where does the
second
> > ref come from?
>
> Well, "<=2" was right once upon a time, but I see that I have no
> tests for this case in the test suite. I could try to track this
> down, but it would be a lot faster if you'd post the test which
> causes the problem.
>
> > Anyway, the FAQ doesn't offer me a solution to the problem - is
there a
> > preferred method for doing what I want to do?
>
> I can't tell what you want to do until you show me the code ;-)
>
> --
> Dave Abrahams
> Boost Consulting
> www.boost-consulting.com
>
>
> _______________________________________________
> C++-sig mailing list
> C++-sig at python.org
> http://mail.python.org/mailman/listinfo/c++-sig
More information about the Cplusplus-sig
mailing list