I'm working on constructing my parse data structure from bison's semantic values. One particular structure is of type std::vector<double>
. I'm curious how the bison internals handle moving semantic values. I tried analyzing the c++.m4 file, and found:
template <typename Base>
inline
void
]b4_parser_class_name[::basic_symbol<Base>::move (basic_symbol& s)
{
super_type::move(s);
]b4_variant_if([b4_symbol_variant([this->type_get ()], [value], [move],
[s.value])],
[value = s.value;])[]b4_locations_if([
location = s.location;])[
}
Unfortunately, I cannot decipher this nearly enough to make out the efficiency of moving a data structure like std::vector, partly owing to my ignorance of the m4 syntax.
Given this in my grammar:
%define api.token.constructor
%define api.value.type variant
%type < std::vector<double> > numlist
...
numlist:
num { $$ = std::vector<double>(); $$.push_back($1); }
| numlist "," num { $$ = $1; $$.push_back($3); }
;
I am uncertain of the performance implications. Note that this will be compiled with a C++98 compiler and not a C++11 compiler; hence there will be no move semantics.
I'm guessing that the $$ = std::vector<double>()
statement could be removed; I assume it would be default-constructed already, but I haven't tested and I'm not sure how bison's internal variant type works. What I'm particularly interested in is the $$ = $1; $$.push_back($3);
Will the vector be copied for every item that is to be added?
I can't determine if this is a case for switching the type to std::vector<double> *
; admittedly, much of the reasoning behind using bison's variant type was to use plain C++ types instead of a union of pointers.
I've also had similar curiosities on a parser that does in fact make use of C++11/14 and in particular std::unique_ptr
. If one line of a left-recursive rule assigned, say $$ = std::make_unique<...>(...)
, would the following be able to do $$ = $1; $$->...
?
I am not a Bison / Yacc expert but you can have a look at the generated code:
{
case 2:
#line 20 "test.yy" // lalr1.cc:846
{ yylhs.value.as< std::vector<double> > () = std::vector<double>();
yylhs.value.as< std::vector<double> > ().push_back(yystack_[0].value.as< double > ()); }
#line 1306 "test.tab.cc" // lalr1.cc:846
break;
case 3:
#line 21 "test.yy" // lalr1.cc:846
{ yylhs.value.as< std::vector<double> > () = yystack_[2].value.as< std::vector<double> > ();
yylhs.value.as< std::vector<double> > ().push_back(yystack_[0].value.as< double > ()); }
#line 1312 "test.tab.cc" // lalr1.cc:846
break;
Which lays inside the parser::parse
method and where yylhs
is a local variable of type stack_symbol_type
and yystack_
is an attribute of the parser
class of type stack_type
(which contains stack_symbol_type
).
It looks like the answer is yes, the whole vector will be copied when you do $$ = $1
, and I don't see how a compiler could optimize this. The declaration of as
is as follows:
template <typename T>
T& as();
With a const
variant, so the affectation is done on the type T
, which in your case is std::vector<double>
, and thus a copy is made. Even if you use c++11 move semantics, a copy would be made because the RHS is not an xvalue
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With