I'm using Bison with C++ and the variants option to construct an AST. If I understand it correctly, Bison always default constructs the semantic values (the AST nodes in my case) before I assign something to them with $$ = .... However, I'd like to keep some (actually most) of my AST nodes non-default constructible, because that would leave them in an invalid state. If I allow default construction of every node, I'd have to rely on runtime checks to ensure that every node I'm working with is valid. Is there any way to make Bison not default construct my nodes and only construct them when I do the $$ = ... action?
Edit: @Some programmer dude requested an example, so here it is, but it's quite long, so sorry about that.
Suppose the following AST structure:
namespace node {
template <typename T>
struct component {
// Not default constructible.
component (T &&);
component (component &&);
component & operator = (T &&);
component & operator = (component &&);
~component ();
T & get ();
T const & get () const;
private:
T * ptr;
};
template <typename T>
struct optional_component {
// Default constructible:
optional_component (): ptr{nullptr} {}
optional_component (T &&);
optional_component (component &&);
optional_component & operator = (T &&);
optional_component & operator = (component &&);
~optional_component ();
T & get ();
T const & get () const;
// Need to check for present value before use:
bool has_value () const { return ptr; }
private:
T * ptr;
};
struct C {
int val;
};
struct A {
// Not default constructible, because component<C> is not.
component<C> bar;
};
struct B {
// Default constructible, because component<C> is too.
optional_component<C> c;
}
}
namespace category {
struct X {
// Not default constructible,
// unless the first type of the variant is default constructible,
// or I provide the default constructor manually.
std::variant<node::A, node::B> node;
};
}
And the following parser definition:
%language "c++"
%define api.value.type variant
%define api.value.automove
%parse-param { category::X & result }
%token A
%token B
%token BB
%token <int> C
%nterm <node::A> node_a
%nterm <node::B> node_b
%nterm <node::C> node_c
%nterm <int> start
%%
start: node_a { result = {.node = $node_a}; } |
node_b { result = {.node = $node_b}; }
node_c: C { $$ = {.val = $1}; }
node_a: A node_c { $$ = {.c = $node_c}; }
node_b: B node_c { $$ = {.c = $node_c}; } |
BB { $$ = {}; }
The generated parser first tries to default construct the nodes. E.g.:
yylhs.value.emplace<node::A>();
And only then assigns the value. E.g:
yylhs.value.as<node::A>() = {.c = YY_MOVE(yystack_[1].value.as<node::C>())};
What I need is to skip the default constructing emplace call and actually construct the node with the given child nodes directly.
I've found this in the generated header file:
# if 201103L <= YY_CPLUSPLUS
/// Instantiate a \a T in here from \a t.
template <typename T, typename... U>
T& emplace (U&&... u) {
return *new (yyas_<T> ()) T (std::forward <U>(u)...);
}
# else
/// Instantiate an empty \a T in here.
template <typename T>
T& emplace () {
return *new (yyas_<T> ()) T ();
}
/// Instantiate a \a T in here from \a t.
template <typename T>
T& emplace (const T& t) {
return *new (yyas_<T> ()) T (t);
}
# endif
So it seems that the emplace method is actually meant to be able to construct the final value directly.