[C++-sig] Python + Boost Python V2 + downcasting
dave at boost-consulting.com
Thu Nov 7 16:09:04 CET 2002
"Nicolas Lelong" <n_lelong at hotmail.com> writes:
>> > I even thought about implementing my own 'isinstance' function but
>> > I'm not familiar enough with boost python & python !!... Any help or
>> > advice will be _greatly_ appreciated :)
>> Why do you need to do this? Such downcasting is usually the mark of a
>> poor design.
> Thanks for your reply,
> I'm aware that this design decision may not be the best - but it
> suits the needs I have for now... I have a tree structure
> representing hierarchical dependencies between elements of a
> graphical system. The objects have a similar base interface that is
> used to navigate through the graph - but each class of item has a
> set a specific properties.
> We decided to use a 'node class' identification system based on C++ RTTI
> because we did not want to reinvent the weel.
OK, I understand.
> Now, if I really can't get the
> correct info in Python, I'll have two choices:
> - propose a change in the node class identification system (i'd rather not)
> - drop Python (i'd rather not !).
> I believe that there must be a way to extract to RTTI info from the
> object holder as it is done to allow the call of a function that
> takes a Derived&(or *) in parameter - it would help me going on in
> my Python evaluation - before having to rethink the whole heart of
> the application ! Could you give me a few hints to create my own
> implementation of 'isinstance' as i don't have much time to
> investigate the boost python v2 internal structures ?
Let's look for other solutions; I just don't think 'isinstance' is
appropriate. You see, Python really is handling a class called 'Base',
even when the pointer actually refers to a 'Derived', and Python's
isinstance just crawls up the Python class hierarchy to find the class
you're looking for.
I notice that you're using return_internal_reference<>() to return the
Base* from the ObjectServer. Does the ObjectServer actually control
the lifetime of the objects, or is it simply holding pointers? I ask
because I don't see a destructor, so in the code you sent,
return_internal_reference<> is inappropriate (maybe you were just
eliminating details for the sake of the post?) -- the point of
return_internal_reference<> is to keep the owning object alive as long
as the owned objects are also alive. If something else is managing
their lifetimes, you may need another mechanism.
Ultimately, I think the best solution is going to be to add a new kind
of ReturnValuePolicy which uses typeid() on the pointer to get the
most-derived class, looks up the corresponding Python class in the
converter::registry, and builds a pointer_holder around it using the
appropriate Python class type. Hmm, looking carefully, this might not
require a new ReturnValuePolicy, and could be as simple as making some
judicious changes to boost/python/object/make_instance.hpp.**
This is just one of whole category of changes needed to support C++
runtime polymorphism really well. I'm currently trying to sort out my
priorities at the moment; if I can secure funding for this work it
will certainly get moved to the top of my queue.
In the meantime, if the ObjectServer is really the owner of the C++
objects, you might consider storing boost::python::object in its
m_objects array. If you convert your Derived objects to
boost::python::object at their point of creation, the full dynamic
type will be preserved for Python.
**There are some open questions... for example, what happens if the
most-derived C++ class hasn't been registered by the user with a
class_<> declaration? It's possible to attempt to find a
most-derived-registered class by using the facilities in
libs/python/src/object/inheritance.cpp, but I'm not sure it's worth
it, and that capability could be added later.
dave at boost-consulting.com * http://www.boost-consulting.com
More information about the Cplusplus-sig