提升精神assign_to_ *定制点

时间:2016-08-11 17:07:12

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

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

这是我正在使用的例子:

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

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

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

struct IntAndChar{ int i; char c; };

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

我以为我可以定义assign_to_attribute_from_value的特化,但我只是这样得到int而第二个元素被删除了。

有人可以给我一些提示,了解它的工作原理吗?

1 个答案:

答案 0 :(得分:3)

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

因此,让我们开始告诉灵魂我们的类型不像容器一样:

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<int, char>,这很乏味且容易出错。

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视为一个容器,这是一个不同的故事)