boost::python and custom smart pointer
Hi, I'd like to have my custom smart_ptr behave like boost::shared_ptr regarding from_python conversions. To_python converstions work fine, but the problem arises when I have an instance to a derived class via a smart_ptr to the base class MySmartPtr<Base> and this has to be passed to a function receiving MySmartPtr<Derived>. If I use boost::shared_ptr this works just fine. I think I'm doing something wrong in MySmartPtr_from_python. I'm pasting a simplified version of my code: ------ BEGIN C++ CODE ------ // Dummy smart pointer class. template <typename T> class MySmartPtr { public: MySmartPtr() : m_P(NULL) { } MySmartPtr(T* a_P) : m_P(a_P) { } T* m_P; }; namespace boost { namespace python { template <typename T> T* get_pointer(MySmartPtr<T> const& a_P) { return a_P.m_P; } template <typename T> struct pointee< MySmartPtr<T> > { typedef T type; }; } // namespace python } // namespace boost template <class T> struct MySmartPtr_from_python { static void* convertible(PyObject* p) { if (p == Py_None) return p; return boost::python::converter::get_lvalue_from_python(p, boost::python::converter::registered< MySmartPtr<T> >::converters); } static void construct(PyObject* source, boost::python::converter::rvalue_from_python_stage1_data* data) { void* const storage = ((boost::python::converter::rvalue_from_python_storage<MySmartPtr<T>
*)data)->storage.bytes;
if (data->convertible == source) // This is because convertible returned p if p was Py_None. new (storage) MySmartPtr<T>(); else new (storage) MySmartPtr<T>( *((MySmartPtr<T>*)data->convertible) ); data->convertible = storage; } }; class Base { public: virtual ~Base() {} }; class Derived : public Base { }; boost::shared_ptr<Base> build_ok() { return boost::shared_ptr<Base>(new Derived()); } MySmartPtr<Base> build_fail() { return MySmartPtr<Base>(new Derived()); } void test_ok(boost::shared_ptr<Derived> a_Derived) {} void test_fail(MySmartPtr<Derived> a_Derived) {} template <typename T> void register_MySmartPtr_conversions() { boost::python::register_ptr_to_python< MySmartPtr<Base> >(); boost::python::converter::registry::insert( &MySmartPtr_from_python<T>::convertible, &MySmartPtr_from_python<T>::construct, boost::python::type_id< MySmartPtr<T> >() ); } BOOST_PYTHON_MODULE(bptest) { boost::python::class_<Base, boost::noncopyable> class_Base("Base", boost::python::no_init); boost::python::register_ptr_to_python< boost::shared_ptr<Base> >(); register_MySmartPtr_conversions<Base>(); boost::python::class_<Derived, boost::noncopyable, boost::python::bases<Base> > class_Derived("Derived", boost::python::no_init); boost::python::register_ptr_to_python< boost::shared_ptr<Derived> >(); register_MySmartPtr_conversions<Derived>(); boost::python::def("build_ok", build_ok); boost::python::def("test_ok", test_ok); boost::python::def("build_fail", build_fail); boost::python::def("test_fail", test_fail); } ------ END C++ CODE ------ This python code fails to work: import bptest bptest.test_ok(bptest.build_ok()) bptest.test_fail(bptest.build_fail()) And this is the error that I get: Boost.Python.ArgumentError: Python argument types in bptest.test_fail(Derived) did not match C++ signature: test_fail(class MySmartPtr<class Derived>) Thanks in advance. -- Gabriel Becedillas Developer CORE SECURITY TECHNOLOGIES
Gabriel Becedillas wrote:
Hi, I'd like to have my custom smart_ptr behave like boost::shared_ptr regarding from_python conversions. To_python converstions work fine, but the problem arises when I have an instance to a derived class via a smart_ptr to the base class MySmartPtr<Base> and this has to be passed to a function receiving MySmartPtr<Derived>. If I use boost::shared_ptr this works just fine. I think I'm doing something wrong in MySmartPtr_from_python. I'm pasting a simplified version of my code:
------ BEGIN C++ CODE ------
// Dummy smart pointer class. template <typename T> class MySmartPtr { public: MySmartPtr() : m_P(NULL) { }
MySmartPtr(T* a_P) : m_P(a_P) { }
T* m_P; };
namespace boost { namespace python {
template <typename T> T* get_pointer(MySmartPtr<T> const& a_P) { return a_P.m_P; }
template <typename T> struct pointee< MySmartPtr<T> > { typedef T type; };
} // namespace python } // namespace boost
template <class T> struct MySmartPtr_from_python { static void* convertible(PyObject* p) { if (p == Py_None) return p;
return boost::python::converter::get_lvalue_from_python(p, boost::python::converter::registered< MySmartPtr<T>
::converters); }
static void construct(PyObject* source, boost::python::converter::rvalue_from_python_stage1_data* data) { void* const storage = ((boost::python::converter::rvalue_from_python_storage<MySmartPtr<T>
*)data)->storage.bytes;
if (data->convertible == source) // This is because convertible returned p if p was Py_None. new (storage) MySmartPtr<T>(); else new (storage) MySmartPtr<T>( *((MySmartPtr<T>*)data->convertible) );
data->convertible = storage; } };
class Base { public: virtual ~Base() {} };
class Derived : public Base { };
boost::shared_ptr<Base> build_ok() { return boost::shared_ptr<Base>(new Derived()); }
MySmartPtr<Base> build_fail() { return MySmartPtr<Base>(new Derived()); }
void test_ok(boost::shared_ptr<Derived> a_Derived) {}
void test_fail(MySmartPtr<Derived> a_Derived) {}
template <typename T> void register_MySmartPtr_conversions() { boost::python::register_ptr_to_python< MySmartPtr<Base> >();
boost::python::converter::registry::insert( &MySmartPtr_from_python<T>::convertible, &MySmartPtr_from_python<T>::construct, boost::python::type_id< MySmartPtr<T> >() ); }
BOOST_PYTHON_MODULE(bptest) { boost::python::class_<Base, boost::noncopyable> class_Base("Base", boost::python::no_init); boost::python::register_ptr_to_python< boost::shared_ptr<Base> >(); register_MySmartPtr_conversions<Base>();
boost::python::class_<Derived, boost::noncopyable, boost::python::bases<Base> > class_Derived("Derived", boost::python::no_init); boost::python::register_ptr_to_python< boost::shared_ptr<Derived> >(); register_MySmartPtr_conversions<Derived>();
boost::python::def("build_ok", build_ok); boost::python::def("test_ok", test_ok);
boost::python::def("build_fail", build_fail); boost::python::def("test_fail", test_fail); }
------ END C++ CODE ------
This python code fails to work:
import bptest
bptest.test_ok(bptest.build_ok()) bptest.test_fail(bptest.build_fail())
And this is the error that I get:
Boost.Python.ArgumentError: Python argument types in bptest.test_fail(Derived) did not match C++ signature: test_fail(class MySmartPtr<class Derived>)
Thanks in advance.
This function should be like this: template <typename T> void register_MySmartPtr_conversions() { boost::python::register_ptr_to_python< MySmartPtr<T> >(); boost::python::converter::registry::insert( &MySmartPtr_from_python<T>::convertible, &MySmartPtr_from_python<T>::construct, boost::python::type_id< MySmartPtr<T> >() ); } But its still failing. -- Gabriel Becedillas Developer CORE SECURITY TECHNOLOGIES
On 3/1/07, Gabriel Becedillas <gabriel.becedillas@corest.com> wrote:
Gabriel Becedillas wrote:
Hi, I'd like to have my custom smart_ptr behave like boost::shared_ptr regarding from_python conversions. To_python converstions work fine, but the problem arises when I have an instance to a derived class via a smart_ptr to the base class MySmartPtr<Base> and this has to be passed to a function receiving MySmartPtr<Derived>. If I use boost::shared_ptr this works just fine. I think I'm doing something wrong in MySmartPtr_from_python. I'm pasting a simplified version of my code:
For one of my project I also needed such functionality. I didn't find the answer. So I implemented pretty quick and simple work-around. I had a factory class, which constructs objects and returns them using custom smart pointer. So, instead of exposing "create" function directly I created small wrapper and exposed it instead: boost::python object create_wrapper( std::string name ){ custom_ptr<base_t> inst = create( name ); //call original function if( dynamic_cast< derived1_t*>( inst.get() ) ){ return boost::python::object( custom_ptr_dynamic_cast<derived1_t>( inst ) ) } else if( ... ){ ... } } This will give your users the behavior you want. Now when I think about this, the better solution would be to introduce small call policy, which derives from the default_call_policies class and contains the if-else+ casting logic. If you use Py++, than it is pretty simple to apply the new call policy on all relevant functions. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
Gabriel Becedillas wrote:
Hi, I'd like to have my custom smart_ptr behave like boost::shared_ptr regarding from_python conversions. To_python converstions work fine, but the problem arises when I have an instance to a derived class via a smart_ptr to the base class MySmartPtr<Base> and this has to be passed to a function receiving MySmartPtr<Derived>. If I use boost::shared_ptr this works just fine. I think I'm doing something wrong in MySmartPtr_from_python. I'm pasting a simplified version of my code:
------ BEGIN C++ CODE ------
// Dummy smart pointer class. template <typename T> class MySmartPtr { public: MySmartPtr() : m_P(NULL) { }
MySmartPtr(T* a_P) : m_P(a_P) { }
T* m_P; };
namespace boost { namespace python {
template <typename T> T* get_pointer(MySmartPtr<T> const& a_P) { return a_P.m_P; }
template <typename T> struct pointee< MySmartPtr<T> > { typedef T type; };
} // namespace python } // namespace boost
template <class T> struct MySmartPtr_from_python { static void* convertible(PyObject* p) { if (p == Py_None) return p;
return boost::python::converter::get_lvalue_from_python(p, boost::python::converter::registered< MySmartPtr<T>
::converters); }
static void construct(PyObject* source, boost::python::converter::rvalue_from_python_stage1_data* data) { void* const storage = ((boost::python::converter::rvalue_from_python_storage<MySmartPtr<T>
*)data)->storage.bytes;
if (data->convertible == source) // This is because convertible returned p if p was Py_None. new (storage) MySmartPtr<T>(); else new (storage) MySmartPtr<T>( *((MySmartPtr<T>*)data->convertible) );
data->convertible = storage; } };
class Base { public: virtual ~Base() {} };
class Derived : public Base { };
boost::shared_ptr<Base> build_ok() { return boost::shared_ptr<Base>(new Derived()); }
MySmartPtr<Base> build_fail() { return MySmartPtr<Base>(new Derived()); }
void test_ok(boost::shared_ptr<Derived> a_Derived) {}
void test_fail(MySmartPtr<Derived> a_Derived) {}
template <typename T> void register_MySmartPtr_conversions() { boost::python::register_ptr_to_python< MySmartPtr<Base> >();
boost::python::converter::registry::insert( &MySmartPtr_from_python<T>::convertible, &MySmartPtr_from_python<T>::construct, boost::python::type_id< MySmartPtr<T> >() ); }
BOOST_PYTHON_MODULE(bptest) { boost::python::class_<Base, boost::noncopyable> class_Base("Base", boost::python::no_init); boost::python::register_ptr_to_python< boost::shared_ptr<Base> >(); register_MySmartPtr_conversions<Base>();
boost::python::class_<Derived, boost::noncopyable, boost::python::bases<Base> > class_Derived("Derived", boost::python::no_init); boost::python::register_ptr_to_python< boost::shared_ptr<Derived> >(); register_MySmartPtr_conversions<Derived>();
boost::python::def("build_ok", build_ok); boost::python::def("test_ok", test_ok);
boost::python::def("build_fail", build_fail); boost::python::def("test_fail", test_fail); }
------ END C++ CODE ------
This python code fails to work:
import bptest
bptest.test_ok(bptest.build_ok()) bptest.test_fail(bptest.build_fail())
And this is the error that I get:
Boost.Python.ArgumentError: Python argument types in bptest.test_fail(Derived) did not match C++ signature: test_fail(class MySmartPtr<class Derived>)
Thanks in advance.
I also forgot to say that upcasts aren't working either. If I return a MySmartPtr<Base> and the concrete type is Derived, boost::python let me use the Derived interface (which is great!), but when this instance has to be passed back to C++ it'll only match an exact MySmartPtr<T> type. It won't do neither an upcast nor a downcast. After checking shared_ptr_from_python a litte bit I realize that convertible(...) is returning a raw T* to the object (when the passed PyObject is not Py_None) and that construct(...) is creating a new boost::shared_ptr from the raw T*. converter::get_lvalue_from_python(p, registered<T>::converters) is used to extract the raw T* from the PyObject. This won't work for me because I need to extract the whole MySmartPtr<T>*, not just the raw T*, in order to copy the MySmartPtr<T> in construct(...). Is there a way to do that ? Please keep in mind that I need to check both for upcasts and downcasts. My PyObject could hold a MySmartPtr<Derived> instance and I'd like to pass it to C++ as a MySmartPtr<Base>, and the other way around (previously checking if a downcast is possible). I really need to make MySmartPtr<T> to behave as boost::shared_ptr<T> regarding conversions. Thanks a lot.
On 3/2/07, Gabriel Becedillas <gabriel.becedillas@corest.com> wrote:
I also forgot to say that upcasts aren't working either. If I return a MySmartPtr<Base> and the concrete type is Derived, boost::python let me use the Derived interface (which is great!), but when this instance has to be passed back to C++ it'll only match an exact MySmartPtr<T> type. It won't do neither an upcast nor a downcast.
Upcast is solvable, look documentation for implicitly_convertible. You also can take a look on complete example of exposing custom smart pointer here: http://language-binding.net/pyplusplus/troubleshooting_guide/smart_ptrs/smar...
After checking shared_ptr_from_python a litte bit I realize that convertible(...) is returning a raw T* to the object (when the passed PyObject is not Py_None) and that construct(...) is creating a new boost::shared_ptr from the raw T*. converter::get_lvalue_from_python(p, registered<T>::converters) is used to extract the raw T* from the PyObject. This won't work for me because I need to extract the whole MySmartPtr<T>*, not just the raw T*, in order to copy the MySmartPtr<T> in construct(...).
Is there a way to do that ? Please keep in mind that I need to check both for upcasts and downcasts. My PyObject could hold a MySmartPtr<Derived> instance and I'd like to pass it to C++ as a MySmartPtr<Base>, and the other way around (previously checking if a downcast is possible). I really need to make MySmartPtr<T> to behave as boost::shared_ptr<T> regarding conversions.
There is only one person who can help you here: Dave Abrahams. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
participants (2)
-
Gabriel Becedillas -
Roman Yakovenko