On 24 Feb 2006, at 22:24, Akim Demaille wrote:
Le 23 févr. 06 à 21:34, Hans Aberg a écrit :
In summary, if one does not need a dynamic polymorphic variable in
the extra-parser computations, then a discriminating union will be
useful. But it does not serve as a replacement in the case a
dynamic polymorphic variable is needed.
There is no problem with storing a polymorphic pointer in a
variant. I fail to see the point of your comment.
There are two different styles of C++ polymorphic programming: The
static template based one, which attempts to resolve as much possible
typing during compile time, but which is limited to the types
provided by the template system. And there is the dynamic "virtual"
class hierarchy one, which provides better generality of types and
makes template programming largely unnecessary, but which requires
free store allocations, which is often relatively slow in compiler
implementations (but which can be overcome the day C++ facilitates GC
implementations). This type of programming circulates about designing
polymorphic types with a specific interface; template programming
circulates much about forcing objects becoming treatable as classes,
that in its turn can be handled by the static typing in the template
system. Template programming is this respect very cumbersome,
relative to the polymorphic hierarchy.
The use of a union is an intermediate, it seems, that avoids free
store allocation, but which cannot be used with any dynamic type, for
example recursive types or those requiring a pointer or handle (an
optimizing implementation may mix unions, pointers and handles). A
handle is required if the types should be able to self-mutate, or if
one is implementing a tracing GC. The latter is currently difficult
to implement in current C++, due to that it is difficult to extract
runtime information about the root set, but I think should be
resolved in the next major C++ revision. There is also the base class
question: When adding a new derived class to a C++ polymorphic class
hierarchy, the base class need not be changed.
Now, when I look at the link you gave
http://www.boost.org/doc/html/variant.html
it gives the class definition:
template<typename T1, typename T2 = unspecified, ...,
typename TN = unspecified>
class variant;
So it means that this will serve as a generalized union, but it will
not cover the aspects of a C++ polymorphic class hierarchy. For
example, recursive types are not be covered; this requires special
template extensions. And, when adding new classes, this variant must
be changed. Also, it gives an example on how to combine two types
'int' and 'string':
class my_visitor : public boost::static_visitor<int>
{
public:
int operator()(int i) const
{
return i;
}
int operator()(const std::string & str) const
{
return str.length();
}
};
And if one has more types, one will have to get back and add function
overloading of operator().
By contrast, in the class hierarchy model, one would have classes like:
struct object {
data operator()(const data&);
};
struct integer : object {
data operator()(const data&);
};
struct string : object {
data operator()(const data&);
};
Then, a class 'data' would maintain a pointer to object. Now, if the
classes integer and string would have been recursive, then operator()
would be automatically able to handle that recursiveness. If I add
new classes, then that would automatically extend the recursiveness
to that, without having to change the interface of the class object.
This is useful when working with massively recursive objects, such as
mathematical expressions requiring generality.
Returning to the question of Bison, it could well be that the use of
variants is very suitable in the implementation of a compiler, where
the set of variant types will be known beforehand and one opts for a
outputting some static code. But I use essentially an interpreter
setup, meaning that the parser produces some C++ objects that will be
computed on the fly. There the C++ polymorphic class hierarchy serves
very well, because I only use the parser to give the user a chance to
construct the object that will later be computed; the computational
engine is in fact much more general than the actual parser, and the
parser will become extended as needed, as the project moves along.
Hans Aberg