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.
I am sorting through this code right now. Am I missing something or is there no documentation in the reference guide about how to register a converter directly with the registry? Additionally, I was looking at the documentation for implicitly_convertible and I am wondering why I can't just use that in my case. I want to call: setObject(ptr<Base> val) with a NoneType. And I have all my objects (ie. Base) wrapped and set with held types of ref_ptr<>'s. RefPtr's are implicitly convertible to ptr<>'s, so If I register this with boost.python using: bp::implicitly_convertible< ret_ptr<Base>, ptr<Base> >(); Then shouldn't boost.python be able to convert NoneType --> ref_ptr<Base> --> ptr<Base> ? From the documentation in implicitly_convertible it seems like this should be the case. But I have to admit I have not found the place in the boost.python code where it handles the case of taking a NoneType and automatically converting it into a NULL smart pointer to start with. -Allen
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; } };
Alex _______________________________________________ C++-sig mailing list C++-sig@python.org http://mail.python.org/mailman/listinfo/c++-sig