One thing I end up doing a lot in our system here is using the preprocessor to instantiate virtual functions when using the Visitor pattern on a large tree heirarchy, and I'd like to propose an extension to templates that might have applicability elsewhere. In the below example, I'm not particularly suggesting a syntax or any keywords or anything. I'm merely using some things that look like keywords and syntax to get the _concept_ of this across. If there's a better or more succinct way to do it...I'm not a language design expert. ;^)
Let's say I have a heirarchy of tree nodes:
class Visitor;
class Base { ... }; // This one is abstract.
class A : public Base
{
...
Base& traverse_left();
Base& traverse_right();
...
};
class B : public Base
{
...
Base& traverse_down();
...
};
class C : public A { ... };
class D : public A { ... };
class E : public B { ... };
class F : public Base { ... };
class G : public F { ... };
class H : public F { ... };
class I : public F { ... };
(Assume that I've declared:
void accept(Visitor& v);
in every class above.)
I'd like to be able to divide this heirarchy into groups of types:
type_group SimilarToA = { A, C, D };
type_group SimilarToB = { B, E };
or even better:
type_group SimilarToA = { A, has_base_class(A) };
type_group SimilarToB = { A, has_base_class(B) };
type_group SimilarToF = { F, has_base_class(F) };
type_group AllTreeNodes = { has_base_class(Base) };
Then, let's say I was doing some visitors, I would like to be able to do apply these type group at compile time:
class Visitor
{
// "apply<List as Name> { code } " would cause the
// compiler to generate code in a fashion similar to
// a template, replacing Name in the code between the
// braces with each name from the list in succession.
//
// So the following clause would expand to:
//
// virtual void Visit(A&) = 0;
// virtual void Visit(B&) = 0;
// ...
// virtual void Visit(H&) = 0;
// virtual void Visit(I&) = 0;
//
apply<AllTreeNodes as T>
{
virtual void Visit(T&) = 0;
}
};
Then I can define traversal (and let's assume it's different for the different classes above):
class TreeTraversal
{
// Leaves don't traverse.
apply<SimilarToF as T>
{
virtual void Visit(T& n) { }
}
// A nodes traverse to the left then the right.
apply<SimilarToA as T>
{
virtual void Visit(T& n)
{
n.traverse_left().accept(this);
n.traverse_right().accept(this);
}
}
// B nodes traverse down.
apply<SimilarToB as T>
{
virtual void Visit(T& n)
{
n.traverse_down().accept(this);
}
}
};
One might define a design rule using this facility:
template<class Base>
class MustVisitAllA : public Base
{
apply<SimilarToA as T>
{
virtual void Visit(T&) = 0;
}
}
And then when compiling a class that inherits from a concrete instantiation of the rule, you'll be forced to implement all the A visits.
One could see that if there were a lot of subclasses of visitor, then this technique could be used quite well to generate methods in them. Right now, to do something similar to the above, I have to use the preprocessor, with all the ugliness and debugging difficulty that this entails. It has been worth it as I probably have a 10:1 ratio of generated methods vs. methods that I have to write (and debug) in our application.
I know that the example I give of use of this is specific to my particular design, but I believe that the ability to apply a list of types to the generation of a section of code arbitrarily as above would be a powerful and useful addition to the language. I don't currently know of a way to generate similar memeber functions (or certain other things) without using the preprocessor.
Thanks for your time. Let me know if this is of any interest.
--
George T. Talbot