Boost :: Spirit обрабатывает неправильно ветки

Я написал код, указанный ниже. Компилятор сообщает мне об ошибке: «ни одна из трех перегрузок не может преобразовать все типы аргументов».

Я использую MSVC 11.0 и Boost 1.51.0. Каждая ветвь выражения для m_oQueryIterationExpression работает правильно, но вместе они не делают. Есть какие-нибудь подсказки?

#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;
}

1

Решение

Ну, дело в том, ваше назначение является неоднозначно, и компилятор просто сообщает вам. Посмотрите, что происходит:

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;

Теперь давайте посмотрим на совместимость атрибутов в некоторых правилах:

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

Это то, что делает инициализацию варианта неоднозначной во второй ветви выражения синтаксического анализатора: это может быть либо!

К счастью, есть очень простое решение: просто сделайте тип явным:

qi::rule<Iterator, typeAB(), ascii::space_type> ruleAB;
ruleAB %= ruleA | ruleB;

Видите ли, заставляя subrules выставлять явный тип-член варианта, вы удаляете все сомнения (точное соответствие подходит компилятору). Этого же эффекта можно достичь, сделав:

ruleAB %= qi::attr_cast_type<typeA, typeA>( '(' >> m_oQueryConcatenationExpression >> ')' >> -(qi::char_("*+?")) ) | ruleB;

Это устраняет необходимость в другом подправиле, но за счет разборчивости, IMO.

Вот ваш полный исходный код, исправленный с помощью подправления: http://liveworkspace.org/code/5a7a8046b713beefba211a6a54219368 (изменить название …)

3

Другие решения

Других решений пока нет …