Redesign for to_python/from_python converters
Some background for this thread is in the following messages: http://article.gmane.org/gmane.comp.python.c++/638 describes the global Boost.Python to/from-python converter registry which associates conversion code with the C++ type(s) being converted. On re-reading this message, I realize it's confusingly worded. Don't be discouraged if it doesn't all sink in. The point is that when a function accepting a C++ argument of type Foo is wrapped, it needs a procedure to extract a Foo from the Python object. The registry associates the code for that procedure with the type Foo. http://article.gmane.org/gmane.comp.python.c++/2044 describes a proposed scheme for dealing with the fact that currently, any given C++ type can have only one to-python conversion in the global registry. This is a problem because one module may use class_<vector<string> >, while another one registers a custom conversion from vector<string> to a Python tuple of strings. http://article.gmane.org/gmane.comp.python.c%2B%2B/2161 describes the general procedure for defining an rvalue from_python converter, and shows why I haven't yet documented how to make user-defined conversions: it's just too hairy a procedure in v2 at the moment. The purpose of this thread is to discuss the design of a new interface for users to interact with to/from-python converters. First, I'd like to discuss some things which I think are desirable. It would be worth knowing which of these are important to the community: 1. Users should have the option to say that certain to/from-python converters shall only have a local effect in a single extension module. 2. It should be possible to optimize away the cost of registry lookups in some cases where it is known that the conversion is defined in the local extension module. This is a completely separate issue from #1. We can have either one without the other. 3. It should be easy for users to explicitly define new conversions. Let's deal with #3 first. You might ask, "what was wrong with the old Boost.Python v1 approach? It sure was simple!" PyObject* to_python(SomeUDT const& x) { ... } SomeUDT from_python(PyObject* p, boost::type<SomeUDT>) { ... } It was simple, but it was basically not legal C++. It worked in so many places because it exploited a very common (and somewhat subtle) bug in C++ compilers, but when if you tried to use more conforming compilers (e.g. CodeWarrior >= 8 or recent EDGs), it would fail to compile. The reasons have to do with the rules for looking up these functions from within templates. Such a scheme can be made to work legally, but you have to resort to weird tricks like asking users to define all their converters before #including Boost.Python headers or adding dummy arguments to the functions for the sake of argument-dependent lookup. There is another problem with the v1 scheme. In particular, it was an important design goal of Boost.Python v2 to eliminate the use of C++ exceptions as part of the process of resolving overloaded C++ functions (in v1, we would throw a special exception to indicate a Python argument couldn't be converted to the corresponding C++ argument type). That means that from_python conversion has to be a 2-phase process: first, determine whether a conversion is possible, then if all arguments can be converted, do all the conversions and call the C++ function. Any user-defined from_python conversion needs to be able to report convertibility separately from actually doing the conversion. There were also some ways in which the v1 interface was hard to use: the 2nd argument to the from_python converter had to exactly match the argument type to any C++ functions being wrapped, so you might need: SomeUDT from_python(PyObject* p, boost::type<SomeUDT>) { ... } SomeUDT& from_python(PyObject* p, boost::type<SomeUDT&>) { ... } SomeUDT const& from_python(PyObject* p, boost::type<SomeUDT const&>) { ... } SomeUDT* from_python(PyObject* p, boost::type<SomeUDT*>) { ... } SomeUDT const* from_python(PyObject* p, boost::type<SomeUDT const*>) { ... } and a few others. So what should the interface look like? Let's first examine the constraints that the C++ language imposes. User-defined converters can be viewed as behavioral customizations of templates in the Boost.Python library. We basically have two approaches available to us for customizing behaviors: 1. Runtime dispatching through virtual functions or function pointers. This is the approach currently taken in Boost.Python v2 for most converters. The converter registry contains pointer to functions which implement the conversions. Some runtime dispatching is always needed for cross-module conversion support, unless you want to repeat the conversion code in every module which needs it (and nobody wants that). There are other reasons to do this having to do with the way dynamic_cast and RTTI are implemented in most compilers. 2. Compile-time customization. This was the default approach in Boost.Python v1: the compiler would look up the appropriate to_/from_python function and insert a call in the function wrapper. It could even be completely inlined. The disadvantage of this approach is that it is heaviliy dependent on code visibility: the customizations have to be visible in every place that wants to take advantage of them. Boost.Python v2 uses compile-time customization only for to-python conversions of "builtin" C++ types for which there can be only one reasonable Python interpretation. For example, const char* and std::string both are converted to Python strings. Ideally, the user could select compile-time customization for to_python conversion of selected types, and additionally choose to export their converters (e.g. to the global registry). I'm really unsure about the value of compile-time customization of from_python converters. The biggest problem with it is that it limits the whole extension module to *one* conversion method for a given C++ type. Normally, a from_python converter is used to convert one Python type to a given C++ type. If another extension module has exported conversions for other Python types to the same C++ type, do you want to be able use them? There are basically two viable techniques for compile-time customization in C++: defining functions to be found by argument-dependent lookup, and template specialization. For various reasons I won't bore you with, I feel that template specialization is the only recourse for Boost.Python. I'll begin discussion of some possible interfaces in a follow-on message. -- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
Hi all, I've just updated to the latest CVS version of boost.python and am having trouble exposing classes with protected destructors. The classes in the library that I am wrapping have protected destructors to enforce the use of the a ref-counting mechanism for object lifetime management. Previously I had done the following to get Python/C++ to work together: ------------------------ template< class T > class RefCountedObject { public: typedef T element_type; explicit RefCountedObject( T* t ) : m_pPtr( t ) { if ( m_pPtr ) m_pPtr->addRef(); } ~RefCountedObject() { if ( m_pPtr ) m_pPtr->releaseRef(); } T& operator*() const { return *get(); } T* operator->() const{ return get(); } T* get() const { return m_pPtr; } private: T* m_pPtr; }; class_< WrappedClass, RefCountedObject< WrappedClass >, ... >( ... ) ... ; ------------------------ Here the RefCountedObject simply increments the refcount of the C++ object to hold it alive while Python is using it. This reference is released when the Python Object (and hence, the RefCountedObject) is destroyed. I thought this was pretty neat, though I'd be happy to get advice on a better solution if one exists. The compile error I get with the latest CVS version is that the destructor of "WrappedClass" is inaccessible at line 22 of boost/python/detail/destroy.hpp. I've tracked this back to rvalue_from_python_data.hpp (line 132): --------------- template <class T> inline rvalue_from_python_data<T>::~rvalue_from_python_data() { if (this->stage1.convertible == this->storage.bytes) python::detail::destroy_referent<ref_type>(this->storage.bytes); } --------------- I won't pretend to know all the details of what is going on here, but I have an inkling that the "if" test would fail in this case, but obviously, the code for the referent destroyer must be compiled anyhow. Any suggestions? Cheers, Daniel Paull
BTW, I also added a template function to get my source to compile: namespace boost { template<class T> T * get_pointer( RefCountedObject<T> const& p ) { return p.get(); } } This should help anyone trying to reproduce the problem. Cheers, Daniel Paull
-----Original Message----- From: c++-sig-admin@python.org [mailto:c++-sig-admin@python.org] On Behalf Of Daniel Paull Sent: Monday, 23 December 2002 11:06 AM To: c++-sig@python.org Subject: [C++-sig] eposing classes with protected destructors
Hi all,
I've just updated to the latest CVS version of boost.python and am having trouble exposing classes with protected destructors. The classes in the library that I am wrapping have protected destructors to enforce the use of the a ref-counting mechanism for object lifetime management.
Previously I had done the following to get Python/C++ to work together:
------------------------ template< class T > class RefCountedObject { public: typedef T element_type; explicit RefCountedObject( T* t ) : m_pPtr( t ) { if ( m_pPtr ) m_pPtr->addRef(); }
~RefCountedObject() { if ( m_pPtr ) m_pPtr->releaseRef(); }
T& operator*() const { return *get(); } T* operator->() const{ return get(); } T* get() const { return m_pPtr; } private: T* m_pPtr; };
class_< WrappedClass, RefCountedObject< WrappedClass >, ... >( ... ) ... ; ------------------------
Here the RefCountedObject simply increments the refcount of the C++ object to hold it alive while Python is using it. This reference is released when the Python Object (and hence, the RefCountedObject) is destroyed.
I thought this was pretty neat, though I'd be happy to get advice on a better solution if one exists.
The compile error I get with the latest CVS version is that the destructor of "WrappedClass" is inaccessible at line 22 of boost/python/detail/destroy.hpp.
I've tracked this back to rvalue_from_python_data.hpp (line 132):
--------------- template <class T> inline rvalue_from_python_data<T>::~rvalue_from_python_data() { if (this->stage1.convertible == this->storage.bytes)
python::detail::destroy_referent<ref_type>(this->storage.bytes);
} ---------------
I won't pretend to know all the details of what is going on here, but I have an inkling that the "if" test would fail in this case, but obviously, the code for the referent destroyer must be compiled anyhow.
Any suggestions?
Cheers,
Daniel Paull
_______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
"Daniel Paull" <dlp@fractaltechnologies.com> writes:
BTW, I also added a template function to get my source to compile:
namespace boost { template<class T> T * get_pointer( RefCountedObject<T> const& p ) { return p.get(); } }
This should help anyone trying to reproduce the problem.
Cheers,
Daniel Paull
Daniel, Unless your compiler doesn't support partial specialization, this function template should be in the same namespace as RefCountedObject so it can be found via argument-dependent lookup. -Dave -- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
I'm using VC6 and it seemed to have problems unless I put it in the boost namespace. I figured that this was only a temporary work around (until I hear otherwise!), so I wasn't too fussed. Should it work with VC6? Nasty hacks like this are a pain and I'd like to avoid them. Cheers, Dan
-----Original Message----- From: c++-sig-admin@python.org [mailto:c++-sig-admin@python.org] On Behalf Of David Abrahams Sent: Monday, 23 December 2002 1:26 PM To: c++-sig@python.org Subject: Re: [C++-sig] eposing classes with protected destructors
"Daniel Paull" <dlp@fractaltechnologies.com> writes:
BTW, I also added a template function to get my source to compile:
namespace boost { template<class T> T * get_pointer( RefCountedObject<T> const& p ) { return p.get(); } }
This should help anyone trying to reproduce the problem.
Cheers,
Daniel Paull
Daniel,
Unless your compiler doesn't support partial specialization, this function template should be in the same namespace as RefCountedObject so it can be found via argument-dependent lookup.
-Dave
-- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
_______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
"Daniel Paull" <dlp@fractaltechnologies.com> writes:
I'm using VC6
In that case, nevermind.
and it seemed to have problems unless I put it in the boost namespace. I figured that this was only a temporary work around (until I hear otherwise!), so I wasn't too fussed.
Should it work with VC6? Nasty hacks like this are a pain and I'd like to avoid them.
No, that's your only option with vc6.
Cheers,
Dan
David Abrahams:
Daniel,
Unless your compiler doesn't support partial specialization, this ^^^^^^^^^^^^^^^^^^^^^^ I meant "argument dependent lookup", here.
function template should be in the same namespace as RefCountedObject so it can be found via argument-dependent lookup.
-Dave
-- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
_______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
_______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
-- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
"Daniel Paull" <dlp@fractaltechnologies.com> writes:
Hi all,
I've just updated to the latest CVS version of boost.python and am having trouble exposing classes with protected destructors. The classes in the library that I am wrapping have protected destructors to enforce the use of the a ref-counting mechanism for object lifetime management.
Previously I had done the following to get Python/C++ to work together:
------------------------ template< class T > class RefCountedObject { public: typedef T element_type; explicit RefCountedObject( T* t ) : m_pPtr( t ) { if ( m_pPtr ) m_pPtr->addRef(); }
~RefCountedObject() { if ( m_pPtr ) m_pPtr->releaseRef(); }
T& operator*() const { return *get(); } T* operator->() const{ return get(); } T* get() const { return m_pPtr; } private: T* m_pPtr; };
class_< WrappedClass, RefCountedObject< WrappedClass >, ... >( ... ) ... ; ------------------------
Here the RefCountedObject simply increments the refcount of the C++ object to hold it alive while Python is using it. This reference is released when the Python Object (and hence, the RefCountedObject) is destroyed.
I thought this was pretty neat, though I'd be happy to get advice on a better solution if one exists.
No, it's a great solution, and the library is designed to work with your smart pointer type in exactly that way.
The compile error I get with the latest CVS version is that the destructor of "WrappedClass" is inaccessible at line 22 of boost/python/detail/destroy.hpp.
I've tracked this back to rvalue_from_python_data.hpp (line 132):
Do you have a complete template instantiation backtrace at the point of the error?
--------------- template <class T> inline rvalue_from_python_data<T>::~rvalue_from_python_data() { if (this->stage1.convertible == this->storage.bytes) python::detail::destroy_referent<ref_type>(this->storage.bytes); } ---------------
I won't pretend to know all the details of what is going on here, but I have an inkling that the "if" test would fail in this case, but obviously, the code for the referent destroyer must be compiled anyhow.
Any suggestions?
Hmm. rvalue_from_python_data<WrappedClass> is used when you wrap a function taking a WrappedClass or WrappedClass const& argument (and a few other places, like extract<WrappedClass>). The assumption is that the converter might need to create a new WrappedClass object, e.g. via some implicit conversion. I assume that's what's happening here and I would be very, very surprised if you were able to compile the same code with Boost 1.29.0. I've been thinking that a fair amount of code in extension modules could be saved if rvalue_from_python_data<T> for non-POD Ts destroyed T by using a function pointer that came from the registry. That way, the destructor would only be generated once, at the point where the rvalue from-python converter were created/registered. That would also solve your problem I believe. This may relate to my other thread about the ease of writing user-defined conversions because the hack of setting the pointer on successful construction might go away. -- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
I thought this was pretty neat, though I'd be happy to get advice on
a
better solution if one exists.
No, it's a great solution, and the library is designed to work with your smart pointer type in exactly that way.
Nice to know I'm on the right track!
The compile error I get with the latest CVS version is that the destructor of "WrappedClass" is inaccessible at line 22 of boost/python/detail/destroy.hpp.
I've tracked this back to rvalue_from_python_data.hpp (line 132):
Do you have a complete template instantiation backtrace at the point of the error?
Here is what VC tells me for one such class: ------------------ c:/developer/include\boost/python/detail/destroy.hpp(22) : error C2248: 'BooleanValue::~BooleanValue' : cannot access protected member declared i n class 'Fg::BooleanValue' ..\../fg/parameters/include\fg/parameters/boolean_value.h(76) : see declaration of 'BooleanValue::~BooleanValue' c:/developer/include\boost/python/detail/destroy.hpp(72) : see reference to function template instantiation 'void __cdecl boost::python:: detail::value_destroyer<0,0>::execute(volatile const class Fg::BooleanValue *)' being compiled ------------------- Interestingly, the same error occurs at a different point in this little example. Here I distilled my code down to the simplest case I could which demonstrated the problem. See compiler output at the end of this snip: ------------------- #include <boost/python.hpp> using namespace boost::python; class A { public: A() {} protected: virtual ~A() {} }; BOOST_PYTHON_MODULE( foo ) { class_< A >( "A" ); } c:/developer/tmp/boost_cvs/boost\boost/python/object/select_holder.hpp(1 02) : error C2248: 'A::~A' : cannot access protected member declared in class 'A' foo.cpp(9) : see declaration of 'A::~A' ------------------------------------ Furthermore, if I declare the class as: class_< A, boost::noncopyable >( "A" ); The error is reported at python/object/value_holder.hpp, line 106. Hope all that's useful...
Hmm. rvalue_from_python_data<WrappedClass> is used when you wrap a function taking a WrappedClass or WrappedClass const& argument (and a few other places, like extract<WrappedClass>). The assumption is that the converter might need to create a new WrappedClass object, e.g. via some implicit conversion. I assume that's what's happening here and I would be very, very surprised if you were able to compile the same code with Boost 1.29.0.
You would be right, there are functions that take a WrappedClass&. In this case, the assumption that a new WrappedClass object may be created is false... at least for my current use case. Get ready to be surprised - this all worked great with a CVS version from a month or two back. I think I last updated in the second half of November. I can't vouch for 1.29.0 though.
I've been thinking that a fair amount of code in extension modules could be saved if rvalue_from_python_data<T> for non-POD Ts destroyed T by using a function pointer that came from the registry. That way, the destructor would only be generated once, at the point where the rvalue from-python converter were created/registered. That would also solve your problem I believe.
I'll have to take your word on that ;) If you do commit a working solution, I'd be more than happy to test it on my code.
This may relate to my other thread about the ease of writing user-defined conversions because the hack of setting the pointer on successful construction might go away.
I did see your email, though I haven't had time to read all the related docs, and my depth of knowledge of python and boost.python is still quite limited... If can contribute ideas or stories based on my experience, I will do so once the silly season it out of the way. Cheers, Dan
"Daniel Paull" <dlp@fractaltechnologies.com> writes:
Do you have a complete template instantiation backtrace at the point of the error?
Here is what VC tells me for one such class:
------------------ c:/developer/include\boost/python/detail/destroy.hpp(22) : error C2248: 'BooleanValue::~BooleanValue' : cannot access protected member declared i n class 'Fg::BooleanValue' ..\../fg/parameters/include\fg/parameters/boolean_value.h(76) : see declaration of 'BooleanValue::~BooleanValue' c:/developer/include\boost/python/detail/destroy.hpp(72) : see reference to function template instantiation 'void __cdecl boost::python:: detail::value_destroyer<0,0>::execute(volatile const class Fg::BooleanValue *)' being compiled -------------------
It looks like vc6 doesn't give the backtrace.
Interestingly, the same error occurs at a different point in this little example. Here I distilled my code down to the simplest case I could which demonstrated the problem. See compiler output at the end of this snip:
------------------- #include <boost/python.hpp> using namespace boost::python;
class A { public: A() {} protected: virtual ~A() {} };
BOOST_PYTHON_MODULE( foo ) { class_< A >( "A" ); }
c:/developer/tmp/boost_cvs/boost\boost/python/object/select_holder.hpp(1 02) : error C2248: 'A::~A' : cannot access protected member declared in class 'A' foo.cpp(9) : see declaration of 'A::~A' ------------------------------------
That's not an interesting case, because in that case we expect Boost.Python to have to destroy an A object directly. You need: class A { public: A() {} protected: virtual ~A() {} friend class RefCountedObject<A>; // <== }; ... class_< A, RefCountedObject<A> >( "A" ); for this demonstration to mean anything. I assume you were making RefCountedObject<A> a friend in your original example.
Furthermore, if I declare the class as:
class_< A, boost::noncopyable >( "A" );
The error is reported at python/object/value_holder.hpp, line 106.
Hope all that's useful...
Nope. Unfortunately it's all irrelevant.
Hmm. rvalue_from_python_data<WrappedClass> is used when you wrap a function taking a WrappedClass or WrappedClass const& argument (and a few other places, like extract<WrappedClass>). The assumption is that the converter might need to create a new WrappedClass object, e.g. via some implicit conversion. I assume that's what's happening here and I would be very, very surprised if you were able to compile the same code with Boost 1.29.0.
You would be right, there are functions that take a WrappedClass&.
I didn't say WrappedClass&; that case doesn't cause rvalue_from_python_data<WrappedClass> to be used. It's only relevant for WrappedClass const& and WrappedClass arguments.
In this case, the assumption that a new WrappedClass object may be created is false... at least for my current use case.
Yes, I understand that.
Get ready to be surprised - this all worked great with a CVS version from a month or two back. I think I last updated in the second half of November. I can't vouch for 1.29.0 though.
Show me a small example, please. I still can't believe it. -- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
David Abrahams <dave@boost-consulting.com> writes:
Hope all that's useful...
Nope. Unfortunately it's all irrelevant.
Hmm. rvalue_from_python_data<WrappedClass> is used when you wrap a function taking a WrappedClass or WrappedClass const& argument (and a few other places, like extract<WrappedClass>). The assumption is that the converter might need to create a new WrappedClass object, e.g. via some implicit conversion. I assume that's what's happening here and I would be very, very surprised if you were able to compile the same code with Boost 1.29.0.
You would be right, there are functions that take a WrappedClass&.
I didn't say WrappedClass&; that case doesn't cause rvalue_from_python_data<WrappedClass> to be used.
It's only relevant for WrappedClass const& and WrappedClass arguments.
In this case, the assumption that a new WrappedClass object may be created is false... at least for my current use case.
Yes, I understand that.
Get ready to be surprised - this all worked great with a CVS version from a month or two back. I think I last updated in the second half of November. I can't vouch for 1.29.0 though.
Show me a small example, please. I still can't believe it.
FWIW, this small example demonstrates what I think is going on in your case. # include <memory> struct A { protected: ~A() {} friend class std::auto_ptr<A>; }; void f1(A&) {}; void f2(A const&) {}; #include <boost/python.hpp> using namespace boost::python; BOOST_PYTHON_MODULE(test_ext) { class_<A, std::auto_ptr<A>, boost::noncopyable>("A") ; def("f1", f1); def("f2", f2); // comment out this line to silence the error } -- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
-----Original Message----- From: c++-sig-admin@python.org [mailto:c++-sig-admin@python.org] On Behalf Of David Abrahams Sent: Monday, 23 December 2002 9:15 PM To: c++-sig@python.org Subject: Re: [C++-sig] eposing classes with protected destructors
"Daniel Paull" <dlp@fractaltechnologies.com> writes:
It looks like vc6 doesn't give the backtrace.
VC and templates are always a drag.
Nope. Unfortunately it's all irrelevant.
Upon re-reading my mail, I agree. Sorry ;)
Get ready to be surprised - this all worked great with a CVS version from a month or two back. I think I last updated in the second half
of
November. I can't vouch for 1.29.0 though.
Show me a small example, please. I still can't believe it.
The code below compiles fine against 1.29.0, but fails as per my original post when compiled against yesterdays CVS version. ----------------SNIP------------------------- #include <boost/python.hpp> using namespace boost::python; template< class T > class RefCountedObject { public: typedef T element_type; explicit RefCountedObject( T* t ) : m_pPtr( t ) { if ( m_pPtr ) m_pPtr->addRef(); } ~RefCountedObject() { if ( m_pPtr ) m_pPtr->releaseRef(); } T& operator*() const { return *get(); } T* operator->() const { return get(); } T* get() const { return m_pPtr; } private: T* m_pPtr; }; // needed for latest CVS version of boost.python namespace boost { // dang VC template<class T> T * get_pointer( RefCountedObject<T> const& p ) { return p.get(); } } class A { public: A( bool val = false ) : m_refCount( 0 ) { m_value = val; } virtual void setValue( bool val ) { m_value = val; } virtual bool getValue() const { return m_value; } virtual void addRef () { ++m_refCount; } virtual void releaseRef () { if( !--m_refCount ) delete this; } virtual int getRefCount () { return 0; } protected: virtual ~A() {} bool m_value; }; BOOST_PYTHON_MODULE( foo ) { class_< A, RefCountedObject< A > >( "A", init< bool >() ) .def( "setValue", &A::setValue ) .def( "getValue", &A::getValue ) ; } ----------------SNIP------------------------- Cheers, Dan
"Daniel Paull" <dlp@fractaltechnologies.com> writes:
Show me a small example, please. I still can't believe it.
The code below compiles fine against 1.29.0,
I still can't believe that.
but fails as per my original post when compiled against yesterdays CVS version.
----------------SNIP------------------------- #include <boost/python.hpp> using namespace boost::python;
template< class T > class RefCountedObject { public: typedef T element_type; explicit RefCountedObject( T* t ) : m_pPtr( t ) { if ( m_pPtr ) m_pPtr->addRef(); } ~RefCountedObject() { if ( m_pPtr ) m_pPtr->releaseRef(); } T& operator*() const { return *get(); } T* operator->() const { return get(); } T* get() const { return m_pPtr; } private: T* m_pPtr; };
// needed for latest CVS version of boost.python namespace boost { // dang VC template<class T> T * get_pointer( RefCountedObject<T> const& p ) { return p.get(); } }
class A { public: A( bool val = false ) : m_refCount( 0 ) { m_value = val; }
VC6 sez: test.cpp(35) : error C2614: 'A' : illegal member initialization: 'm_refCount' is not a base or member test.cpp(38) : error C2065: 'm_refCount' : undeclared identifier
virtual void setValue( bool val ) { m_value = val; } virtual bool getValue() const { return m_value; } virtual void addRef () { ++m_refCount; } virtual void releaseRef () { if( !--m_refCount ) delete this; } virtual int getRefCount () { return 0; } protected: virtual ~A() {} bool m_value; };
BOOST_PYTHON_MODULE( foo ) { class_< A, RefCountedObject< A > >( "A", init< bool >() ) .def( "setValue", &A::setValue ) .def( "getValue", &A::getValue ) ; } ----------------SNIP-------------------------
-- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
Ah bugger, I pasted the duff implementation of the ref counting to add a flavour of completeness but missed the member var. Add "int m_refCount" to the class def and away we go! Dan
-----Original Message----- From: c++-sig-admin@python.org [mailto:c++-sig-admin@python.org] On Behalf Of David Abrahams Sent: Tuesday, 24 December 2002 11:41 AM To: c++-sig@python.org Subject: Re: [C++-sig] eposing classes with protected destructors
"Daniel Paull" <dlp@fractaltechnologies.com> writes:
Show me a small example, please. I still can't believe it.
The code below compiles fine against 1.29.0,
I still can't believe that.
but fails as per my original post when compiled against yesterdays CVS version.
----------------SNIP------------------------- #include <boost/python.hpp> using namespace boost::python;
template< class T > class RefCountedObject { public: typedef T element_type; explicit RefCountedObject( T* t ) : m_pPtr( t ) { if ( m_pPtr ) m_pPtr->addRef(); } ~RefCountedObject() { if ( m_pPtr ) m_pPtr->releaseRef(); } T& operator*() const { return *get(); } T* operator->() const { return get(); } T* get() const { return m_pPtr; } private: T* m_pPtr; };
// needed for latest CVS version of boost.python namespace boost { // dang VC template<class T> T * get_pointer( RefCountedObject<T> const& p ) { return p.get(); } }
class A { public: A( bool val = false ) : m_refCount( 0 ) { m_value = val; }
VC6 sez:
test.cpp(35) : error C2614: 'A' : illegal member initialization: 'm_refCount' is not a base or member test.cpp(38) : error C2065: 'm_refCount' : undeclared identifier
virtual void setValue( bool val ) { m_value = val; } virtual bool getValue() const { return m_value; } virtual void addRef () { ++m_refCount; } virtual void releaseRef () { if( !--m_refCount ) delete this; } virtual int getRefCount () { return 0; } protected: virtual ~A() {} bool m_value; };
BOOST_PYTHON_MODULE( foo ) { class_< A, RefCountedObject< A > >( "A", init< bool >() ) .def( "setValue", &A::setValue ) .def( "getValue", &A::getValue ) ; } ----------------SNIP-------------------------
-- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
_______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
"Daniel Paull" <dlp@fractaltechnologies.com> writes:
Ah bugger, I pasted the duff implementation of the ref counting to add a flavour of completeness but missed the member var. Add "int m_refCount" to the class def and away we go!
Fixed now in CVS. -- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
It all works at this end. Thanks for the fix. Dan
-----Original Message----- From: c++-sig-admin@python.org [mailto:c++-sig-admin@python.org] On Behalf Of David Abrahams Sent: Tuesday, 24 December 2002 12:49 PM To: c++-sig@python.org Subject: Re: [C++-sig] eposing classes with protected destructors
"Daniel Paull" <dlp@fractaltechnologies.com> writes:
Ah bugger, I pasted the duff implementation of the ref counting to add a flavour of completeness but missed the member var. Add "int m_refCount" to the class def and away we go!
Fixed now in CVS.
-- David Abrahams dave@boost-consulting.com * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution
_______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig
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. 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? Anyway, the FAQ doesn't offer me a solution to the problem - is there a preferred method for doing what I want to do? Thanks, Daniel Paull
participants (2)
-
Daniel Paull -
David Abrahams