Boost::spirit processes rule with branches incorrectly
-
08-07-2021 - |
Question
I wrote the code listed below. The compiler reports me an error: 'none of the 3 overloads could convert all the argument types'.
I use MSVC 11.0 and Boost 1.51.0. Each branch of expression for m_oQueryIterationExpression
works correctly but together they do not. Any clues?
#include <boost/spirit/include/qi.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/variant/recursive_variant.hpp>
namespace Ns
{
struct Regexp { std::string m_strEntity; };
struct String { std::string m_strEntity; };
struct Identifier { std::string m_strEntity; };
typedef int Number;
typedef std::string Operation;
typedef boost::variant<Regexp, Number, String, Identifier> Operand;
typedef boost::tuple<Operation, boost::optional<std::vector<Operand> > > OperationWithOperands;
struct QueryConcatenation;
typedef boost::tuple<boost::recursive_wrapper<QueryConcatenation>, boost::optional<char> > typeA;
typedef std::vector<std::vector<OperationWithOperands> > typeB;
typedef boost::variant<typeA, typeB> QueryIteration;
struct QueryConcatenation {
typedef std::vector<QueryIteration> TEntity;
TEntity m_oEntity;
};
}
int main()
{
using namespace Ns;
namespace qi = boost::spirit::qi;
qi::rule<char*, QueryConcatenation()> m_oQueryConcatenationExpression;
qi::rule<char*, QueryIteration()> m_oQueryIterationExpression;
qi::rule<char*, std::vector<std::vector<OperationWithOperands> >() > m_oQueryNode;
m_oQueryIterationExpression %=
qi::attr_cast<typeA, typeA>( '(' >> m_oQueryConcatenationExpression >> ')' >> -(qi::char_("*+?")) )
| m_oQueryNode;
}
Solution
Well, the thing is, your assignment is ambiguous, and the compiler is just letting you know. See what happens:
typedef boost::tuple<
boost::recursive_wrapper<STreeConstructionRuleQueryConcatenation>,
boost::optional<char>
> typeA;
typedef std::vector<std::vector<STreeConstructionRuleOperationWithOperands> > typeB;
typedef boost::variant<typeA, typeB> typeAB;
typedef typeAB STreeConstructionRuleQueryIteration;
Now let's see about attribute compatibility in some rules:
qi::rule<Iterator, typeA(), ascii::space_type> ruleA;
qi::rule<Iterator, typeB(), ascii::space_type> ruleB;
qi::rule<Iterator, typeAB(), ascii::space_type> ruleAB;
// these are intended to compile, and they do:
ruleA %= ( '(' >> m_oQueryConcatenationExpression >> ')' >> -(qi::char_("*+?")) );
ruleB %= m_oQueryNode;
// so far, so good
// ---- Now, ideally we like the assignments to be mutually exclusive:
// This fails to assign, good! :
// ruleB %= ( '(' >> m_oQueryConcatenationExpression >> ')' >> -(qi::char_("*+?")) );
// But HERE is the surprise:
ruleA %= m_oQueryNode; // uhoh m_oQueryNode can be assigned to a typeA attribute as well
This is what is making the initialization of the variant ambiguous on the second branch of the parser expression: it could be either!
Fortunately, there is a very simple solution: just make the type explicit:
qi::rule<Iterator, typeAB(), ascii::space_type> ruleAB;
ruleAB %= ruleA | ruleB;
You see, by making subrules expose the explicit type-member of the variant, you remove all doubt (an exact match is fine to the compiler). The same effect could be reached doing:
ruleAB %= qi::attr_cast_type<typeA, typeA>( '(' >> m_oQueryConcatenationExpression >> ')' >> -(qi::char_("*+?")) ) | ruleB;
This removes the need for another subrule, but at the cost of legibility, IMO.
Here is your full original code, fixed with a subrule: http://liveworkspace.org/code/5a7a8046b713beefba211a6a54219368 (change the naming...)