[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