[C++-sig] Mapping an entire data structure over to a single Python instance. (Newbie question)

Gerard Murphy g.j.murphy at sageserpent.com
Sat Aug 13 15:06:00 CEST 2005


Hello,

I'm evaluating the potential use of the Boost.Python facilities. I've
looked though the tutorial and *some* of the detailed API documentation
available from www.boost.org, and so far it all looks very promising.

However...

I have a rather strange use planned for the C++ to Python interface that
isn't covered by the tutorial. The detailed API documentation doesn't
seem to answer my question either; so I'd like to put it to the mailing
list, as I'm sure that this idea has already occurred to somebody in the
past.

*** Background ***

What I'd like to do is map an entire data structure over to a single
Python instance, so that individual properties of the Python object
would correspond to parts of the data structure.

To add spice to this, the data structure is in a sense weakly typed:
different parts of it are composed out of very different C++ types. The
data structure is dynamically built (otherwise there wouldn't be in
issue as I could map a C-style structure or getter/setter class over to
a Python class using the techniques described in the tutorial).


I'll make this a little bit more concrete:-

1. I have what is effectively a weakly typed wrapper around a set of
different C++ types using external polymorphism:-


class AbstractValue
{
public:
  ~virtual AbstractValue();
};

template <typename StrongType>
class ConcreteValue: public AbstractValue
{
public:
  // Use compiler-generated destructor.
  ConcreteValue(const StrongType &valueRepresentation):
    _valueRepresentation(valueRepresentation)

  StrongType _valueRepresentation;
};


Instances of these make their way into a map:-

typedef std::map<std::string, boost::shared_ptr<AbstractValue> >
DataStructure;


2. Various things in the system populate these maps in different ways,
so that from one map instance to another, not only do the associated
values change, but so do the number of associations, the keys and the
actual types of the associated values.

However, lots of these maps share the same common structure in terms of
numbers of associations, keys and associated types. These could be
thought of as instances of several classes, where each class captures
the commonality of structure.

These classes effectively come into being as the system runs: they
cannot be defined at compile time as conventional C++ classes.

No, I can't just write the entire system in Smalltalk (or Python for
that matter) ... :-)

3. I'd like to expose these conceptual classes to Python, mapping over
these instances of 'DataStructure' mentioned above to Python objects -
but with the Python class changing depending on what instance of
'DataStructure' is being mapped.

The classes of the Python objects would be dynamically created by
construction of a 'class_' object:-




  // Somewhere this line will be repeatedly executed...

  boost::python::class_<DataStructure> classObject(name);

I'm assuming that although the class_ object will be destroyed at each
repetition, the corresponding Python class lives on afterwards. It isn't a
problem if this is not the case, as I could always manage these objects in a
long-lived container or on the heap or whatever.


What I intend to do is to dynamically add properties to 'classObject'
that use the name of the property to fetch the appropriate associated
value from the map. This would be done once per repetition, so that each
repetition would create a brand new Python class.

To be a bit more specific, I want to call 'classObject.add_property()' with
an application of boost::bind to a template function instantiation that
takes a name, looks up the associated value in the map, dynamically
casts to the template type parameter and returns the value.

I can ask an exemplar map to walk itself so that on behalf of a set of maps
sharing the same common structure, it will populate a single class
object with the appropriate properties.


*** Now for the questions ***

OK, that's what I want to do ... but can I do the following first:-

i) Can I construct more than one instance of 'class_<DataStructure>'
using lots of different names as arguments at each construction call?  I
get the impression from the documentation that each template
instantiation of 'class_ <X>' for a given X might well be a singleton
class in terms of usage - only one constructed instance per class
template instantiation, if you know what I mean!

ii) If I *can* have lots of instances of 'class_<DataStructure>', how
does the framework know how to map instances of 'DataStructure' to the
appropriate Python class - this seems to be implicitly done rather than
explictly done.

What I'm driving at here is taking an *existing* instance of
DataStructure and wrapping it up as a 'boost::python::object' whose
Python type is one of these dynamically constructed classes. This is in
contrast to using the 'class_<DataStructure>' object to create the instance
of 'boost::python::object' - I would definitely like to avoid doing this if
possible, although I appreciate that it is usually the obvious way to go.

NOTE: I'm assuming that the properties can be defined using *any*
function object that would take an instance of 'DataStructure' and
return an appropriately typed value - I am planning on using
'boost::bind' and an appropriate trampoline template function to do
this.



In the meantime, I'll have a longer trawl through the API documentation and
perhaps the implementation source, but I'd appreciate any help from old
hands to shorten the process.

Many thanks in advance,

Gerard




















More information about the Cplusplus-sig mailing list