Re: [C++-sig] Mapping an entire data structure over to a single Python instance. (Newbie question)
"Dave Abrahams" <dave at boost-consulting.com> writes:
"Gerard Murphy" <g.j.murphy@sageserpent.com> writes:
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.
Is that an important feature of the system?
It's a 'very nice to have' - but I could fallback to just treating all maps as being, well, dictionaries in Python. :-) Something I didn't mention in the original post is that I would like to add certain methods onto certain of these classes, and that the client code on the Python side would know in advance what set of methods the instances would be expected to possess: these objects would be passed over to Python in sets where each member of the set would respond to the same set of method calls. In effect I'm saying that if objects 'A' and 'B' both possess the attributes 'foo' and 'bar', then they would acquire a method 'doSomethingImportantInvolvingFooAndBar'. Object 'C' on the other hand that possesses property 'baz' acquires another method, say 'shutdownReactorAfterInternalDialogueWithBaz'. ;-) 'A' and 'B' would be passed together to one Python function, 'C' would be passed to another. All three of these objects might have other attributes that could be optionally accessible on the Python side, but are not vital for the correct operation of the system.
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);
No, that won't work.
What you can do is use Python's metaclass API. http://gnosis.cx/publish/programming/metaclass_1.html
Export your C++ DataStructure class once.
object data_structure = class_<DataStructure>( ... ) ... ;
Then get its metaclass:
object meta = object( handle<>( borrowed(data_structure.ptr()->ob_type) ) );
Now you can manufacture distinct subclasses on-the-fly:
object new_subclass = meta( "name-of-subclass", python::make_tuple(data_structure), dict());
Excellent!
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.
I suggest you don't do that either. It would be better to implement a __getattr__ method in your exported DataStructure.
object getattr(DataStructure& ds, std::string name) { return object( ...lookup name in ds... ); }
object data_structure = class_<DataStructure>( ... ) .def("__getattr__", getattr) ;
For this you'll need to export class_<AbstractValue> and class_<ConcreteValue<T> > for each ConcreteValue<T> you are using.
Ah - yes, that's much better.
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.
Doing it with add_property is going to be a bear. Why would you want to do that?
As a Python novice, I had automatically assumed that properties were the only form of abstraction available on instance variable access (I had C# in mind). Now that you've introduced me to '__getattr__' and the like, I've seen the light. :-)
*** 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?
Nope
No problem, that's now obsolete in the light of using the metaclass object to produce more classes. Many thanks, Gerard
participants (1)
-
g.j.murphy@sageserpent.com