So, I ran into strange behaviour with boost::spirit::x3 as supplied in boost 1.59:
I defined a 'dynamic' symbol table via:
struct instructions : x3::symbols<OpCode> {
instructions()
{
name("instructions");
}
void set_instruction_set(const std::unordered_map<std::string, OpCode>& instruction_set) {
for (const auto& var : instruction_set) {
add(var.first, var.second);
}
}
} instructions_parser;
OpCode
is defined as
struct OpCode
{
std::string mnemonic;
std::vector<...> variants;// actual type in vector<> not important.
};
now, with the symbol table embedded in the neccessary rules, when parsing an input string like
mov r2 r1
mov r1 @80
the resulting ast only contains the first mov
with its operands.
The second mov is missing but the operands are correctly parsed.
This may look as followed, when printing the resulting AST:
mov r2 r1
r1 @80
With the debugger I located the source of the error in symbols.hpp in symbol_parser::parse()
:
template <typename Iterator, typename Context, typename Attribute>
bool parse(Iterator& first, Iterator const& last
, Context const& context, unused_type, Attribute& attr) const
{
x3::skip_over(first, last, context);
if (value_type* val_ptr
= lookup->find(first, last, get_case_compare<Encoding>(context)))
{
x3::traits::move_to(*val_ptr, attr); //<- the error originates from here
return true;
}
return false;
}
with move_to
beeing:
template <typename T>
inline void move_to(T& src, T& dest)
{
if (boost::addressof(src) != boost::addressof(dest))
dest = std::move(src);
}
As you can see, the src
which is my OpCode instance added in the symbol_parser is moved. This means after the first call its empty again and that's why only the first instructions appears. It's, simply put, moved out of the symbol table.
Now finally my question: Is this a bug or am I making a mistake?
As suggested by sehe my workaround as an answer:
I found a temporary workaround: By declaring the template parameter as const one can suppress move semantics. The copy-ctor is then called.
I.E.: x3::symbols<const std::string>