TITLE: avoiding downcasting in design (Source: comp.lang.c++.moderated, 7 Apr 2001) DUNGEN: Marcel van den Dungen > I've written a script-parser but have some questions about the > design of the code. > > I have an Expression class, containing a pointer to an Operator > object and 2 pointers to operand Expression objects. Derived > from the Expression class are 3 classes: BooleanExpression, > NumericExpression and StringExpression. Derived from > BooleanExpression there are classes like BooleanLiteral and > BooleanVariable, so objects of those classes are valid operands to > the Operator objects. The derived expression classes can be > evaluated to obtain the resulting value. > In order to get this to work, I introduced a type variable in the > Expression class, and use dynamic_cast to downcast to a type which > can be evaluated. The Expression class itself cannot be evaluated, > but the BooleanExpression class results in a bool, the > NumericExpression class results in an integer etc. > > I would however have liked to use multiple dispatch instead of the > dynamic_casts, but don't know how to make this work with multiple > operands to an operator. WESTON: "Bill Weston" I have been reading up on this, and wrote a multi dispatcher which I am emailing to you (4 below). Sorry the code isn't very pretty but you can see the idea. Here is a survey of what I found out (motivated by my knee jerk reaction to (3 below) that it *must* be possible within the language...) 1) Your program sounds similar to an example in James Coplien's Styles and Idioms book, in which he designs classes to model filters in electronics, that can either do their thing to a raw signal, producing an output signal, or to another filter, producing a composite filter. He uses type fields although his application looked more like single dispatch than multiple, in that there was only one object in the bracket of his operator()'s , although the exact type of subject on which the operator was called dictated the behaviour, so it was MD. As I recall he preferred the typefields to the other solution of turning the function call around and using the virtual function call twice ( like the visitor pattern and as described in Stroustrup's Design and Evolution, and in Scott Meyers' More Effective C++) because with swapping around all the sibling classes have to be updated when a new sibling Expression is added to the design. 2) Scott Meyers went on to write a table based multi-dispatcher based on type_info -- sort of maintain your own vtable, say a map with signature as key and function pointer as value. Andrei Alexandrescu expands impressively on this solution, which he calls a logarithmic dispatcher, in Modern C++ Design. Andrei's logarithmic dispatcher with his 'trampoline' functions to avoid the downcasts solve your basic problem, I reckon. The drawbacks are that it does not dipatch non-exact signature matches polymorphically without some more hacking (see my code and below for my 'solutions' to this); that logarithmic dispatching takes longer the more operands you have ( I guess the clue is in the name :-); that it is a bit tricky to get it to work with more than one possible base class per argument slot -- (but you wouldn't need this for your app, I guess); and that you have to explicitly tell the dispatcher about all the options. I reckon one outght to be able to design a hash to make the dispatch amortized constant time, but that is tricky because a new non-exact match causes the table to be revised on the hoof, and not portable because the type_info's are a bit slippery, neither the name() nor the address are guaranteed to be unique, apparently. 3) A more radical solution is provided by Julian Smith's Cmm, C++MultiMethods, (http://www.op59.net/cmm/) which adds an extra preparsing step to compilation, thereby resolving all the dispatches without needing to register the functions (or rather setting up a table and enough helper functions to enable the actual resolution to happen at runtime -- there is still a lookup to do at runtime), and enabling proper polymorphic dispatching to be done, even with unrelated base classes. I think there is a link to another pre parsing solution from Jules' site. 4) It is possible to do proper polymorphic dispatch with a single base per argument slot, without scanning all the source however, because the dispatcher does not need to know all the class hierarchies, just the relationships between the candidate classes. It can either work these out using the template trick with an overloaded foo(...) and foo(Base*), or it can store an instance of each class and use dynamic_cast(). [snip] _______________________________________________ cpptips mailing list http://cpptips.hyperformix.com