Hi. I am working on some project that defines smart pointer and uses it allover the place. I have a problem for some reason I am not able to call functions that takes the smart pointer instantiation as argument. The error I get is ArgumentError: ArgumentError: Python argument types in custom_smart_ptr_classes.ref_get_value(add_x_t) did not match C++ signature: ref_get_value(my_smart_ptr_t<controllers::controller_i> {lvalue} a) Code high level: template<T> class my_smart_ptr_t{...} struct controller_i{ virtual int get_value() const = 0; }; struct add_x_t : controller_i{ int m_value; virtual int get_value( ){ return m_value + get_add_value(); } virtual int get_add_value(){ return 0; } }; int get_value( my_smar_ptr_t<controller_i> c ){ return c->get_value(); } In order to export this code, I wrote next code: namespace boost{ namespace python{ controller_i* get_pointer( my_smart_ptr_t< controller_i > const& p ) { return p.get(); } template <> struct pointee< my_smart_ptr_t< controller_i > > { typedef controller_i type; }; //same code for add_x_t class ... }} Because both classes contains virtual function I create class-wrapper for every class. Classes and function registration is pretty simple. After this I add next registration code: register_ptr_to_python< my_smart_ptr_t< controller_i > >(); register_ptr_to_python< my_smart_ptr_t< add_x_t > >(); implicitly_convertible< my_smart_ptr_t< add_x_t >, my_smart_ptr_t< controller_i > >(); Python code: import my_module add_x = my_module.add_x_t() my_module.get_value( add_x ) # raises the error I described earlier. I think, I exposed my classes "by the book". May be I am missing something? Thanks in advance. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
"Roman Yakovenko" <roman.yakovenko@gmail.com> writes:
Code high level:
template<T> class my_smart_ptr_t{...}
struct controller_i{ virtual int get_value() const = 0; };
struct add_x_t : controller_i{ int m_value; virtual int get_value( ){ return m_value + get_add_value(); } virtual int get_add_value(){ return 0; } };
int get_value( my_smar_ptr_t<controller_i> c ){ return c->get_value(); }
In order to export this code, I wrote next code:
namespace boost{ namespace python{
controller_i* get_pointer( my_smart_ptr_t< controller_i > const& p ) { return p.get(); }
template <> struct pointee< my_smart_ptr_t< controller_i > > { typedef controller_i type; };
//same code for add_x_t class ...
}}
Because both classes contains virtual function I create class-wrapper for every class. Classes and function registration is pretty simple. After this I add next registration code:
register_ptr_to_python< my_smart_ptr_t< controller_i > >(); register_ptr_to_python< my_smart_ptr_t< add_x_t > >(); implicitly_convertible< my_smart_ptr_t< add_x_t >, my_smart_ptr_t< controller_i > >();
Python code:
import my_module add_x = my_module.add_x_t() my_module.get_value( add_x ) # raises the error I described earlier.
I think, I exposed my classes "by the book". May be I am missing something?
From this I can't understand what you're doing or what errors you're getting. Please do what I tell everyone to do: make a _complete_, _minimal_ example that reproduces your problem and post that.
-- Dave Abrahams Boost Consulting www.boost-consulting.com
On 8/31/06, David Abrahams <dave@boost-consulting.com> wrote:
From this I can't understand what you're doing or what errors you're getting. Please do what I tell everyone to do: make a _complete_, _minimal_ example that reproduces your problem and post that.
I attached 3 files. This is complete and minimal example. custom_smart_ptr.h file contains smart pointer class definition. custom_smart_ptr_classes_to_be_exported.hpp file contains definition of 2 classes: "base" and "derived" classes. This file also defines "get_value" function. this function is called from Python. custom_smart_ptr_classes.cpp file contains Boost.Python bindings. Python code is next: import custom_smart_ptr_classes as cspc d = cspc.derived( 2 ) cspc.get_value( d ) The last line raises next exception: ArgumentError: Python argument types in custom_smart_ptr_classes.get_value(derived) did not match C++ signature: get_value(my_smart_ptr_t<base> a) Thank you. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
On 8/31/06, Roman Yakovenko <roman.yakovenko@gmail.com> wrote:
On 8/31/06, David Abrahams <dave@boost-consulting.com> wrote:
From this I can't understand what you're doing or what errors you're getting. Please do what I tell everyone to do: make a _complete_, _minimal_ example that reproduces your problem and post that.
I attached 3 files. This is complete and minimal example.
custom_smart_ptr.h file contains smart pointer class definition.
custom_smart_ptr_classes_to_be_exported.hpp file contains definition of 2 classes: "base" and "derived" classes. This file also defines "get_value" function. this function is called from Python.
custom_smart_ptr_classes.cpp file contains Boost.Python bindings.
Python code is next: import custom_smart_ptr_classes as cspc d = cspc.derived( 2 ) cspc.get_value( d )
The last line raises next exception:
ArgumentError: Python argument types in custom_smart_ptr_classes.get_value(derived) did not match C++ signature: get_value(my_smart_ptr_t<base> a)
Thank you.
Can somebody help me with this issue? Thanks in advance. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
Python code is next: import custom_smart_ptr_classes as cspc d = cspc.derived( 2 ) cspc.get_value( d )
The last line raises next exception:
ArgumentError: Python argument types in custom_smart_ptr_classes.get_value(derived) did not match C++ signature: get_value(my_smart_ptr_t<base> a)
Thank you.
Can somebody help me with this issue?
Thanks in advance.
In my experience, boost python needs a lot of hand holding to work well with custom smart pointers. I think the first thing you need here is an rvalue from-python conversion for your smart pointers. Register a conversion for each smart_ptr<T> that you expose to python. And make sure you do it for smart_ptr<T>, not smart_ptr<wrapper<T> > in the case of polymorphic stuff. I (semi)automate this in a custom def visitor. Alex
On 9/5/06, Alex Mohr <amohr@pixar.com> wrote:
In my experience, boost python needs a lot of hand holding to work well with custom smart pointers.
I think the first thing you need here is an rvalue from-python conversion for your smart pointers.
I don't know and understand how I should do it, really :-(. I took a look on shared_ptr_from_python class. I understand the code which is written in construct function. static void construct(PyObject* source, rvalue_from_python_stage1_data* data) { ... new (storage) shared_ptr<T>( static_cast<T*>(data->convertible), shared_ptr_deleter(handle<>(borrowed(source))) ); ... } Boost.Python creates new instance of shared_ptr with custom deleter. This way it says to shared_ptr that the object is already managed. The smart pointer, the project use, does not provide this functionality. More over, I don't understand what data->convertible will contain.
Register a conversion for each smart_ptr<T> that you expose to python. And make sure you do it for smart_ptr<T>, not smart_ptr<wrapper<T> > in the case of polymorphic stuff.
I think I do it: register_ptr_to_python< my_smart_ptr_t< base > >(); register_ptr_to_python< my_smart_ptr_t< derived > >(); implicitly_convertible< my_smart_ptr_t< derived >, my_smart_ptr_t< base > >(); This is what yoy mean, right? Many thanks for your help. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
I don't know and understand how I should do it, really :-(.
Boost.Python creates new instance of shared_ptr with custom deleter. This way it says to shared_ptr that the object is already managed.
I believe it's just a hack to do what I call "object identity" -- which is just tracking the association between a C++ instance and a python object. When converting a shared ptr to-python, boost python checks to see if it has its special deleter, and will use that to produce the python object rather than constructing a new one. This is a fragile scheme. It fails frequently for shared ptrs, as has been discussed on this list before, and it doesn't work at all for custom shared pointers. Anyway, I consider object identity to be a different issue from the one we're currently discussing. But I do think you should address it in py++. We can talk about that later.
The smart pointer, the project use, does not provide this functionality. More over, I don't understand what data->convertible will contain.
If I recall correctly (I'm not looking at the code) data->convertible is the result that the convertible method returned, so in this case it's a raw pointer to the C++ instance.
I think I do it:
register_ptr_to_python< my_smart_ptr_t< base > >(); register_ptr_to_python< my_smart_ptr_t< derived > >(); implicitly_convertible< my_smart_ptr_t< derived >, my_smart_ptr_t< base > >();
This is what yoy mean, right?
No, these are to-python conversions. I'm talking about rvalue from-python conversions. Have a look at the last few messages in the concurrent thread on this list with subject "Is there a way to automatically convert a smart_ptr to a held_type (try 2)". I posted a message to that thread which has some (untested) smart pointer rvalue from-python conversion code in it. Actually -- it probably looks something like shared_ptr_from_python.
Many thanks for your help.
You're welcome. Alex
On 9/6/06, Alex Mohr <amohr@pixar.com> wrote:
I don't know and understand how I should do it, really :-(.
Boost.Python creates new instance of shared_ptr with custom deleter. This way it says to shared_ptr that the object is already managed.
I believe it's just a hack to do what I call "object identity" -- which is just tracking the association between a C++ instance and a python object. When converting a shared ptr to-python, boost python checks to see if it has its special deleter, and will use that to produce the python object rather than constructing a new one.
This is a fragile scheme. It fails frequently for shared ptrs, as has been discussed on this list before, and it doesn't work at all for custom shared pointers.
Anyway, I consider object identity to be a different issue from the one we're currently discussing. But I do think you should address it in py++. We can talk about that later.
I am not completely understand what you are talking about ( object identity ). I will be glad to discuss it with you and add the appropriate functionality to Py++, really.
The smart pointer, the project use, does not provide this functionality. More over, I don't understand what data->convertible will contain.
If I recall correctly (I'm not looking at the code) data->convertible is the result that the convertible method returned, so in this case it's a raw pointer to the C++ instance.
I think I do it:
register_ptr_to_python< my_smart_ptr_t< base > >(); register_ptr_to_python< my_smart_ptr_t< derived > >(); implicitly_convertible< my_smart_ptr_t< derived >, my_smart_ptr_t< base > >();
This is what yoy mean, right?
No, these are to-python conversions. I'm talking about rvalue from-python conversions. Have a look at the last few messages in the concurrent thread on this list with subject "Is there a way to automatically convert a smart_ptr to a held_type (try 2)".
I posted a message to that thread which has some (untested) smart pointer rvalue from-python conversion code in it. Actually -- it probably looks something like shared_ptr_from_python.
I read the thread "Is there a way to automatically convert a smart_ptr to a held_type (try 2)" and still I am not able to get to a solution :-(. I have to say that I am lost. I implemented rvalue converter, I am registering custom to_python_converter - nothing happens. I attach the minimal example. Any help is welcome. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
"Roman Yakovenko" <roman.yakovenko@gmail.com> writes:
On 8/31/06, David Abrahams <dave@boost-consulting.com> wrote:
From this I can't understand what you're doing or what errors you're getting. Please do what I tell everyone to do: make a _complete_, _minimal_ example that reproduces your problem and post that.
I attached 3 files. This is complete and minimal example.
It's hardly minimal! A minimal example would have no "documentation" strings, no use of named arguments (bp::arg(...)) and no use of things that you get by default anyway, like default_call_policies. It would not contain extraneous implicit conversions from int. This is a problem with many code generators: they create an unreadable mess for anyone who wants to understand the result. Part of the reason many people despise the MFC Wizards. I suggest you fix Py++ so it doesn't add vacuous "documentation" and other needless cruft. In a minimal example, there would be just one .cpp file, one .py file, and if possible, a Jamfile I can use to build and run the test. BTW, your #include guards use identifiers that are reserved to the C++ implementation. I made your example work. I had to add an operator* to your smart pointer, although I think that uncovers a bug in Boost.Python: we should be using get_pointer where it needed the operator*, instead. I don't know if this meets your needs or not, but the result is enclosed. HTH, -- Dave Abrahams Boost Consulting www.boost-consulting.com
On 9/12/06, David Abrahams <dave@boost-consulting.com> wrote:
I made your example work. I had to add an operator* to your smart pointer, although I think that uncovers a bug in Boost.Python: we should be using get_pointer where it needed the operator*, instead.
I don't know if this meets your needs or not, but the result is enclosed.
Thank you for your time and help. Unfortunately it did not help :-(. I reworked the example, base on your comments. I had to add next conversion: bp::implicitly_convertible< smart_ptr_t< base_wrapper_t >, smart_ptr_t< base_i > >(); There are still 2 problems: I am not able to call functions that take reference and const reference to smart_ptr_t< base > instance. int ref_get_value( smart_ptr_t<base_i>& a ){ return a->get_value(); } I am unable to call this function, I've got ArgumentError exception int const_ref_get_value( const smart_ptr_t<base_i>& a ){ return a->get_value(); } I am unable to call this function, I've got "segmentation fault" on Linux. I attached test.py file that checks all the functions. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
"Roman Yakovenko" <roman.yakovenko@gmail.com> writes:
On 9/12/06, David Abrahams <dave@boost-consulting.com> wrote:
I made your example work. I had to add an operator* to your smart pointer, although I think that uncovers a bug in Boost.Python: we should be using get_pointer where it needed the operator*, instead.
I don't know if this meets your needs or not, but the result is enclosed.
Thank you for your time and help.
Unfortunately it did not help :-(. I reworked the example, base on your comments.
I had to add next conversion: bp::implicitly_convertible< smart_ptr_t< base_wrapper_t >, smart_ptr_t< base_i > >();
There are still 2 problems: I am not able to call functions that take reference and const reference to smart_ptr_t< base > instance.
FWIW, reference and const reference are totally separate cases. const reference is essentially the same as pass-by-value; it only requires an rvalue conversion. Mutable references require an lvalue conversion.
int ref_get_value( smart_ptr_t<base_i>& a ){ return a->get_value(); }
I am unable to call this function, I've got ArgumentError exception
Naturally; there is no instance of smart_ptr_t<base_i> anywhere in the Python object for the reference to bind to. The rules are the same as in C++: int f(smart_ptr_t<base_i>& x) { return 0; } smart_ptr_t<base_wrapper_t> y; int z = f(y); // fails to compile
int const_ref_get_value( const smart_ptr_t<base_i>& a ){ return a->get_value(); }
I am unable to call this function, I've got "segmentation fault" on Linux.
Where does the segmentation fault occur? Is 'a' a valid pointer within const_ref_get_value?
I attached test.py file that checks all the functions.
Try building a minimal example that does nothing more than to reproduce your segmentation fault. Then we'll see if the problem is in Boost.Python or in your code. -- Dave Abrahams Boost Consulting www.boost-consulting.com
On 9/13/06, David Abrahams <dave@boost-consulting.com> wrote:
FWIW, reference and const reference are totally separate cases. const reference is essentially the same as pass-by-value; it only requires an rvalue conversion. Mutable references require an lvalue conversion.
int ref_get_value( smart_ptr_t<base_i>& a ){ return a->get_value(); }
I am unable to call this function, I've got ArgumentError exception
Naturally; there is no instance of smart_ptr_t<base_i> anywhere in the Python object for the reference to bind to. The rules are the same as in C++:
int f(smart_ptr_t<base_i>& x) { return 0; } smart_ptr_t<base_wrapper_t> y; int z = f(y); // fails to compile
Thank you for explanation.
int const_ref_get_value( const smart_ptr_t<base_i>& a ){ return a->get_value(); }
I am unable to call this function, I've got "segmentation fault" on Linux.
Where does the segmentation fault occur? Is 'a' a valid pointer within const_ref_get_value?
Try building a minimal example that does nothing more than to reproduce your segmentation fault. Then we'll see if the problem is in Boost.Python or in your code.
I built one and I don't like what I see. Let me explain. smart_ptr_t{...}; struct derived_t{ virtual int get_value(){ return 11;} }; int val_get_value( smart_ptr_t<derived_t> a ){ return a->get_value(); } int const_ref_get_value( const smart_ptr_t<derived_t>& a ){ if( a.get() ) return a->get_value(); else return -1; } namespace boost{ namespace python{ get_pointer definition pointee definition } } Registration is pretty simple: bp::class_< derived_t, smart_ptr_t<derived_t> >( "derived_t" ) .def( "get_value", &::derived_t::get_value ); bp::def( "const_ref_get_value", &::const_ref_get_value ); bp::def( "val_get_value", &::val_get_value ); Python test code also simple: inst = cspc_ext.derived_t() cspc_ext.val_get_value(inst) cspc_ext.const_ref_get_value(inst) # <= in this line I get segmentation fault. Now the mistery I saw: 1. If I remove "virtual" from derived_t::get_value everything works as expected. 2. If I comment "cspc_ext.val_get_value(inst)" expression, than cspc_ext.const_ref_get_value(inst) works. I attached the source code. I am using compiler g++ 4.0.3 on Ubuntu, CVS version of boost. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
"Roman Yakovenko" <roman.yakovenko@gmail.com> writes:
Python test code also simple:
inst = cspc_ext.derived_t() cspc_ext.val_get_value(inst) cspc_ext.const_ref_get_value(inst) # <= in this line I get segmentation fault.
AFAICT this example doesn't work as packaged (the Jamroot doesn't look for test.py). Your smart pointer is buggy; it has only a default copy ctor. The enclosed works. -- Dave Abrahams Boost Consulting www.boost-consulting.com
On 9/19/06, David Abrahams <dave@boost-consulting.com> wrote:
"Roman Yakovenko" <roman.yakovenko@gmail.com> writes:
Python test code also simple:
inst = cspc_ext.derived_t() cspc_ext.val_get_value(inst) cspc_ext.const_ref_get_value(inst) # <= in this line I get segmentation fault.
AFAICT this example doesn't work as packaged (the Jamroot doesn't look for test.py).
It is possible. I don't use bjam. It is much easier to use scons. ( At least I don't have to define twice the environment of the project )
Your smart pointer is buggy; it has only a default copy ctor. The enclosed works.
Thank you very much for your help. I will try to do much better job next time. -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
"Roman Yakovenko" <roman.yakovenko@gmail.com> writes:
On 9/19/06, David Abrahams <dave@boost-consulting.com> wrote:
"Roman Yakovenko" <roman.yakovenko@gmail.com> writes:
AFAICT this example doesn't work as packaged (the Jamroot doesn't look for test.py).
It is possible. I don't use bjam. It is much easier to use scons.
It would be a lot nicer for those giving you support if you could make the effort to be sure that your test cases actually work as intended.
( At least I don't have to define twice the environment of the project )
Sorry, I don't know what that means. Could you explain? -- Dave Abrahams Boost Consulting www.boost-consulting.com
On 9/19/06, David Abrahams <dave@boost-consulting.com> wrote:
"Roman Yakovenko" <roman.yakovenko@gmail.com> writes:
On 9/19/06, David Abrahams <dave@boost-consulting.com> wrote:
"Roman Yakovenko" <roman.yakovenko@gmail.com> writes:
AFAICT this example doesn't work as packaged (the Jamroot doesn't look for test.py).
It is possible. I don't use bjam. It is much easier to use scons.
It would be a lot nicer for those giving you support if you could make the effort to be sure that your test cases actually work as intended.
Sorry, I really tried to create test that you will have nothing to do, but it is not that easy to use bjam. I think, I will prepare a template, that will help me and other people to report problems.
( At least I don't have to define twice the environment of the project )
Sorry, I don't know what that means. Could you explain?
Using Python I can define "environment.py" file, where I can define all relevant settings: Boost.Python and Python header files directory, compilation settings for bindings and for the original project. Then I can reuse these configuration from Py++ and from scons. It is very easy to setup the script on different machines and different users. Example: http://svn.sourceforge.net/viewvc/pygccxml/pyplusplus_dev/environment.py?vie... http://svn.sourceforge.net/viewvc/pygccxml/pyplusplus_dev/examples/pyboost_d... -- Roman Yakovenko C++ Python language binding http://www.language-binding.net/
participants (3)
-
Alex Mohr -
David Abrahams -
Roman Yakovenko