【问题标题】:boost spirit assign_to_* customization points提升精神assign_to_*自定义点
【发布时间】:2016-08-11 17:07:12
【问题描述】:

我正在尝试了解提升精神assign_to_* 自定义点的工作原理。

这是我正在使用的一个示例:

我在语法规则中有这个解析器:

int_ >> lit(':') >> char_

我希望将结果放入这个结构中:

struct IntAndChar{ int i; char c; };

(这只是一个使用自定义点的示例,所以我不会使用BOOST_FUSION_ADAPT_STRUCT 或语义操作。)

我以为我可以定义 assign_to_attribute_from_value 的特化,但我只能通过这种方式获得 int 并且第二个元素被删除。

有人可以给我一个提示以了解它是如何工作的吗?

【问题讨论】:

    标签: c++ boost boost-spirit boost-spirit-qi


    【解决方案1】:

    您不想分配给属性¹。相反,您希望将boost::fusion::vector2<int, char>转换IntAndChar

    因此,让我们开始告诉 Spirit 我们的类型不是容器式的:

    template<>
    struct is_container<IntAndChar, void> : mpl::false_ { };
    

    接下来,告诉它如何在我们的属性的原始和熟化形式之间转换 e:

    template<>
    struct transform_attribute<IntAndChar, fusion::vector2<int, char>, qi::domain, void> {
        using Transformed = fusion::vector2<int, char>; 
        using Exposed     = IntAndChar;
        using type        = Transformed;
    
        static Transformed pre(Exposed&) { return Transformed(); }
    
        static void post(Exposed& val, Transformed const& attr) {
            val.i = fusion::at_c<0>(attr);
            val.c = fusion::at_c<1>(attr);
        }
    
        static void fail(Exposed&) {}
    };
    

    就是这样!不过有一个问题。除非您触发转换,否则它将不起作用。 The docs say:

    由 Qi 规则、语义动作和 attr_cast 调用,[...]

    1。使用qi::rule(不是很有帮助)

    所以这里有一个使用rule的解决方案:

    Live On Coliru

    int main() {
        using It = std::string::const_iterator;
    
        qi::rule<It, boost::fusion::vector2<int, char>(), qi::space_type> rule = qi::int_ >> ':' >> qi::char_;
        //qi::rule<It, IntAndChar(), qi::space_type> rule = qi::attr_cast(qi::int_ >> ':' >> qi::char_);
    
        for (std::string const input : { "123:a", "-4 :   \r\nq" }) {
            It f = input.begin(), l = input.end();
            IntAndChar data;
    
            bool ok = qi::phrase_parse(f, l, rule, qi::space, data);
    
            if (ok)     std::cout << "Parse success: " << data.i << ", " << data.c << "\n";
            else        std::cout << "Parse failure ('" << input << "')\n";
            if (f != l) std::cout << "Remaining unparsed input: '" << std::string(f, l) << "'\n";
        }
    }
    

    打印:

    Parse success: 123, a
    Parse success: -4, q
    

    当然,这种方法需要你拼出boost::fusion::vector2&lt;int, char&gt;,这既繁琐又容易出错。

    2。使用qi::attr_cast

    您可以使用qi::attr_cast 来触发转换:

    qi::rule<It, IntAndChar(), qi::space_type> rule = qi::attr_cast<IntAndChar, boost::fusion::vector2<int, char> >(qi::int_ >> ':' >> qi::char_);
    // using deduction:
    qi::rule<It, IntAndChar(), qi::space_type> rule = qi::attr_cast<IntAndChar>(qi::int_ >> ':' >> qi::char_);
    // using even more deduction:
    qi::rule<It, IntAndChar(), qi::space_type> rule = qi::attr_cast(qi::int_ >> ':' >> qi::char_);
    

    CAVEAT应该起作用。但是,由于非常微妙的行为(错误?),您需要在那里深度复制 Proto 表达式树,以便它在没有 Undefined Behaviour 的情况下工作:

    qi::rule<It, IntAndChar(), qi::space_type> rule = qi::attr_cast(qi::copy(qi::int_ >> ':' >> qi::char_));
    

    综合起来,我们甚至可以不用qi::rule

    Live On Coliru

    int main() {
        using It = std::string::const_iterator;
    
        for (std::string const input : { "123:a", "-4 :   \r\nq" }) {
            It f = input.begin(), l = input.end();
            IntAndChar data;
    
            bool ok = qi::phrase_parse(f, l, qi::attr_cast(qi::copy(qi::int_ >> ':' >> qi::char_)), qi::space, data);
    
            if (ok)     std::cout << "Parse success: " << data.i << ", " << data.c << "\n";
            else        std::cout << "Parse failure ('" << input << "')\n";
            if (f != l) std::cout << "Remaining unparsed input: '" << std::string(f, l) << "'\n";
    
        }
    }
    

    打印

    Parse success: 123, a
    Parse success: -4, q
    

    ¹(除非您想将 IntAndChar 视为一个容器,这是另一回事)

    【讨论】:

    • 现在清晰多了。它运行良好。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多