Spirit.X3 с лямбдой, возвращающей разные типы парсеров

Здесь я пытаюсь преобразовать строковый литерал в число, где базовый спецификатор является динамическим:

#include <string>
#include <boost/spirit/home/x3.hpp>

namespace ast {
    struct literal {
        enum base_specifier { bin, oct, hex };

        base_specifier  base;
        std::string     literal;
    };
}

namespace x3 = boost::spirit::x3;


template<typename T>
auto as = [](auto p) { return x3::rule<struct _, T>{} = x3::as_parser(p); };


template <typename TargetT>
std::pair<bool, TargetT> convert(ast::literal const& node)
{
    auto const parse = [](ast::literal::base_specifier base, auto const& literal) {

        using base_specifier = ast::literal::base_specifier;

        auto const parser = [](base_specifier base) {
            switch(base) {
                case base_specifier::bin: {
                    using parser_type = x3::uint_parser<TargetT, 2>;
                    parser_type const p = parser_type{};
                    return as<TargetT>( p );
                }
                case base_specifier::oct: {
                    using parser_type = x3::uint_parser<TargetT, 8>;
                    parser_type const p = parser_type{};
                    return as<TargetT>( p );
                }
                case base_specifier::hex: {
                    using parser_type = x3::uint_parser<TargetT, 16>;
                    parser_type const p = parser_type{};
                    return as<TargetT>( p );
                }
                default:
                    abort();
            }
        };

        auto iter      = std::begin(literal);
        auto const end = std::cend(literal);
        TargetT attribute;

        bool parse_ok  = x3::parse(iter, end, parser(base), attribute);

        return std::make_tuple(parse_ok && (iter == end), attribute);
    };

    // other complex stuff here

    return parse(node.base, node.literal);
}


int main()
{
    ast::literal literal{ ast::literal::hex, "AFFE" };
    auto const [parse_ok, result] = convert<int32_t>(literal);
}

но это не удается с:

error: return type 'rule_definition<_, uint_parser<[...], 8, [2 * ...]>, [2 * ...]>' must match previous return type
  'rule_definition<_, uint_parser<[...], 2, [2 * ...]>, [2 * ...]>' when lambda expression has unspecified explicit return type

Сообщение об ошибке ясно, но у меня нет решения для получения желаемого поведения. От диспетчеризации типа выбора динамического парсера на основе базового спецификатора зависят другие операции, поэтому этот подход полезен для моего варианта использования. Было бы особенно полезно, если бы решение также работало с реальными/двойными типами и их политиками. Я предполагаю, что это больше проблема C++ по духу.

Кстати, можно ли таким образом вернуть конкретный парсер? Требуется копия экземпляра, что может быть неэффективным, не так ли? Существуют ли другие/лучшие способы обработки обнаружения диапазона/переполнения TargetT простым сбоем синтаксического анализа?

Для удобства код также можно найти в Wandbox.


person Olx    schedule 21.05.2018    source источник
comment
По прошествии времени я пришел к выводу, что простого пути нет. Можно было бы использовать варианты или даже не возвращать парсер. Просто вызовите x3::parse и верните результаты (с/без варианта для посещения). Даже встроенный синтаксис parser(base) был бы хорош для правила синтаксического анализатора вызывающей стороны...   -  person Olx    schedule 29.05.2018


Ответы (1)


То, что вы пытаетесь выполнить, невозможно по дизайну, потому что:

returning different parser types

^ Это не разрешено языком C++.


rule предназначен для простой оболочки над вашей грамматикой с дополнительной информацией (тип атрибута и флаг принудительного типа атрибута). При вызове оператора присваивания rule он вернет rule_definition, который содержит сигнатуру фактической грамматики (поэтому существует BOOST_SPIRIT_DEFINE).

Если вы пришли в X3 из Qi, я понимаю ваше замешательство. В Qi rule создает реальный синтаксический анализатор и сохраняет его в boost::function (при этом теряется любая статическая информация и подразумевается стоимость вызова виртуальной функции), но X3 rule ближе к Qi subrule.

person Nikita Kniazev    schedule 01.07.2018
comment
Возможно, вы могли бы упомянуть x3::any_parser в своем ответе, что кажется тем, что нужно спрашивающему. - person llonesmiz; 26.08.2018
comment
@llonesmiz Это хороший хак! Пожалуйста, опубликуйте это как ответ на вопрос. - person Nikita Kniazev; 26.08.2018