Previous: , Up: C++ Semantic Values   [Contents][Index]


10.1.4.2 C++ Variants

Bison provides a variant based implementation of semantic values for C++. This alleviates all the limitations reported in the previous section, and in particular, object types can be used without pointers.

To enable variant-based semantic values, set the %define variable api.value.type to variant (see %define Summary). Then %union is ignored; instead of using the name of the fields of the %union to “type” the symbols, use genuine types.

For instance, instead of:

%union
{
  int ival;
  std::string* sval;
}
%token <ival> NUMBER;
%token <sval> STRING;

write:

%token <int> NUMBER;
%token <std::string> STRING;

STRING is no longer a pointer, which should fairly simplify the user actions in the grammar and in the scanner (in particular the memory management).

Since C++ features destructors, and since it is customary to specialize operator<< to support uniform printing of values, variants also typically simplify Bison printers and destructors.

Variants are stricter than unions. When based on unions, you may play any dirty game with yylval, say storing an int, reading a char*, and then storing a double in it. This is no longer possible with variants: they must be initialized, then assigned to, and eventually, destroyed. As a matter of fact, Bison variants forbid the use of alternative types such as ‘$<int>2’ or ‘$<std::string>$’, even in midrule actions. It is mandatory to use typed midrule actions (see Typed Midrule Actions).

Method on value_type: T& emplace<T> ()
Method on value_type: T& emplace<T> (const T& t)

Available in C++98/C++03 only. Default construct/copy-construct from t. Return a reference to where the actual value may be stored. Requires that the variant was not initialized yet.

Method on value_type: T& emplace<T, U> (U&&... u)

Available in C++11 and later only. Build a variant of type T from the variadic forwarding references u....

Warning: We do not use Boost.Variant, for two reasons. First, it appeared unacceptable to require Boost on the user’s machine (i.e., the machine on which the generated parser will be compiled, not the machine on which bison was run). Second, for each possible semantic value, Boost.Variant not only stores the value, but also a tag specifying its type. But the parser already “knows” the type of the semantic value, so that would be duplicating the information.

We do not use C++17’s std::variant either: we want to support all the C++ standards, and of course std::variant also stores a tag to record the current type.

Therefore we developed light-weight variants whose type tag is external (so they are really like unions for C++ actually). There is a number of limitations in (the current implementation of) variants:

As far as we know, these limitations can be alleviated. All it takes is some time and/or some talented C++ hacker willing to contribute to Bison.


Previous: C++ Unions, Up: C++ Semantic Values   [Contents][Index]