带有lambda的Spirit.X3返回不同的解析器类型

时间:2018-05-21 07:35:19

标签: c++ boost-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找到代码。

1 个答案:

答案 0 :(得分:0)

由于以下原因,您尝试执行的操作无法实现:

returning different parser types

^ C ++语言不允许。


rule旨在作为语法的简单包装,带有附加信息(属性类型和强制属性类型标志)。通过调用rule的赋值运算符,它将返回rule_definition,其中包含实际语法的签名(这就是为什么存在BOOST_SPIRIT_DEFINE的原因)。

如果您是从Qi来的X3,我会理解您的困惑。在Qi中,rule进行了一个实际的解析器并将其存储到boost::function中(同时丢失了任何静态信息并暗示了虚拟函数调用的成本),但是X3 rule更接近Qi的{{3 }}。