On 9/5/06, Alex Mohr <amohr@pixar.com> wrote:
Thanks Alex, this worked great. I had to change the Py_INCREF to a bp::incref but otherwise things are all good.
But now I have a rather strange followup question.
It seems that Boost.Python is doing some magic behind the scenes to convert an object coming from C++ as a smart pointer whose wrapped pointer is NULL into a Python "None". The is great, and was a wonderful surprise. But now I need to find a way to make it happen in the other direction.
How does boost.python convert a parameter of "None" into an object/smart_ptr to pass to C++?
It happens in the from_python conversion. It just checks for None and (I believe) default-constructs a the pointer object. Is it the case that default-constructing one of your smart pointers does not produce the intended NULL? It probably wouldn't be hard to add a customization (paralleling get_pointer and pointee) to boost python. But in the meantime...
I need to find a way to call a C++ method like:
void setObject(ptr<Base> obj);
from Python and call it in Python as "setObject(None)" and instruct boost.python to convert this into a call behind the scenes in C++ to:
setObject(NullPtr);
where "NullPtr" is a global const value defined by the library that is used to signify a NULL shared pointer throughout the system. (the reason it needs something like this is a little complex, but it is not my library so I can't change this part much.)
Alternatively, I could probably make it work if I could get boost.python to make a call like:
setObject(ref_ptr<Base>(NULL) )
as I think I could get the library to automatically convert ref_ptr<Base>(NULL) into a NullPtr.
Any ideas?
I think you can do either version by registering your own rvalue from_python converters for all your pointer types. You can do this in the def_visitor along with the to_python conversion we discussed earlier.
Just replace the relevant bits below ("Deal with the "None" case") with whatever you want.
Again, I'm just typing this so it may not compile, and no guarantees of course...
template <class Ptr> struct smart_ptr_from_python { typedef typename boost::python::pointee<Ptr>::type Pointee; smart_ptr_from_python() { converter::registry::insert(&convertible, &construct, type_id<Ptr>()); } private: static void *convertible(PyObject *p) { // can always produce a pointer from None. if (p == Py_None) return p; // Otherwise, we can do it if we can get the pointee out. void *result = converter::get_lvalue_from_python (p, converter::registered<Pointee>::converters); return result; }
static void construct(PyObject* source, converter:: rvalue_from_python_stage1_data* data) { void* const storage = ((converter::rvalue_from_python_storage<Ptr>*)data)-> storage.bytes; // Deal with the "None" case. if (data->convertible == source) new (storage) Ptr(); // Or whatever you want. else new (storage) Ptr(static_cast<Pointee*>(data->convertible)); data->convertible = storage; } };
This code allowed the setObject(None) to work now. Unfortunately it broke the functionality that had been working where I could call setObject(PythonObject). More details (extracted from above): I have: // set method void setObject(ptr<Base> obj); // Implicit conversion for ref_ptr to ptr bp::implicitly_convertible< ref_ptr<Base>, ptr<Base> ); // From original e-mail: this works to automatically convert // ptr<Base> returns into ref_ptr<Base> as the held type bp::to_pyton_converter< ptr<Base>, ptr_to_python< ptr<Base> >() // Most recent discussion: way to get None from python as ptr<Base> // - This works to let me pass None into wrapped setObject method bp::converter::registry::insert(&convertible, &construct, bp::type_id<ptr<Base> >()); The problem is that now when I try to call something like this in python: new_obj = objtype.create() setObject(None) setObject(new_obj) Both of these calls to setObject try to convert the object to a ptr<Base> using the manually registered converter. The converter only really knows how to deal with the conversion from a None type. In the case of a real object I just want boost.python to convert the held type (ref_ptr<Base> ) into a ptr<Base> using the implicit conversion. How do I get this to work? Can I have multiple converters registered and if so how do I tell boost.python to only use the manually created one for the NoneType? -Allen