Boost Spirit X3:“_val”和“_attr”有什么区别?

Rav*_*ven 3 c++ boost boost-spirit-x3

我正在研究 Boost Spirit X3,但正在努力完全掌握语义操作中使用x3::_val和之间的区别x3::_attr,以从传递的上下文中提取属性。官方文档指出

_val:对直接或间接调用解析器 p 的最内层规则的属性的引用
_attr:对解析器 p 的属性的引用

这对我没什么帮助。我研究了一些并偶然发现了/sf/answers/4319223961/,其中指出

x3::_val(ctx) 类似于 qi::_val
x3::_attr(ctx) 类似于 qi::_0 (对于简单解析器则为 qi::_1)

但不幸的是,我没能弄清楚什么qi::_valqi::_0/qi::_1,或者更准确地说它们的区别是什么。

最后,我还找到了/sf/answers/3778032191/,其中指出

它们在x3::_val特定x3::_attr情况下的情况取决于 make_attribute/transform_attribute 特征。默认情况下,它们将引用相同的值,直到您具有具有不同属性类型的嵌套规则

这似乎与我迄今为止进行的实验一致——除了我还没有设法_attr_val产生不同的值。

尽管我还无法理解它们的区别,但它似乎与我见过的所有示例相当相关,使用语义动作来计算给定计算的结果(参见例如此处)似乎总是用作_attr更全局的状态,而_val似乎是刚刚解析的事物的即时状态。例如

[](auto& ctx) { _val(ctx) = _val(ctx) * _attr(ctx); }
Run Code Online (Sandbox Code Playgroud)

然而,尽管如此,我仍然不太能够指出这两个属性之间语义上的确切差异。有人可能会尝试重新表述 Boost 的文档并举一个例子来说明差异实际上很重要/可见吗?

seh*_*ehe 5

_val:对直接或间接调用解析器 p 的最内层规则的属性的引用
_attr:对解析器 p 的属性的引用

既然这对你没有帮助,我们来说明一下。

我喜欢调用合成属性_attr(访问器):

Spirit 中的解析器和生成器是完全归因的。Spirit.Qi 解析器总是公开特定于其类型的属性。这称为综合属性因为它是从表示匹配的输入序列的成功匹配中返回的。

例如,数字解析器(例如int_double_)返回从匹配的输入序列转换而来的int 或值。double其他原始解析器组件具有其他直观的属性类型,例如int_which hasintascii::char_which haschar

另一方面,调用解析器时有绑定到规则的属性,即_val(ctx)到规则的属性,即. 这取决于您在什么上下文中使用它。

这是一个说明差异的演示程序:

住在科里鲁

#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/x3.hpp>
#include <iostream>
using boost::core::demangle;
namespace x3 = boost::spirit::x3;

namespace Parser {
    using boost::fusion::at_c;

    auto action = [](auto& ctx) {
        using Synthesized = decltype(_attr(ctx));
        using Bound       = decltype(_val(ctx));

        std::cout << "synthesized: " << demangle(typeid(Synthesized).name()) << "\n";
        std::cout << "bound:       " << demangle(typeid(Bound).name()) << "\n";

        if constexpr (!std::is_same_v<x3::unused_type, std::decay_t<Bound>>) {
            at_c<0>(_val(ctx)) = at_c<0>(_attr(ctx));
            at_c<1>(_val(ctx)) = at_c<1>(_attr(ctx));
        }
    };

    auto expr = (x3::int_ >> x3::int_)[action];
    template <typename T> auto rule = x3::rule<struct Tag, T>{"rule"} = expr;
} // namespace Parser

template <typename T = x3::unused_type> void test() {
    static constexpr std::string_view input = "123 234";

    if constexpr (std::is_same_v<x3::unused_type, T>) {
        phrase_parse(begin(input), end(input), Parser::rule<T>, x3::space);
        std::cout << " -> (no attribute)\n\n";
    } else {
        T attr;
        phrase_parse(begin(input), end(input), Parser::rule<T>, x3::space, attr);

        std::cout << " -> " << boost::fusion::as_vector(attr) << "\n\n";
    }
}

int main() {
    test();
    test<boost::fusion::deque<int, int>>();
    test<std::pair<int, int>>();
    test<std::tuple<int, int>>();
}
Run Code Online (Sandbox Code Playgroud)

印刷:

synthesized: boost::fusion::deque<int, int>
bound:       boost::spirit::x3::unused_type
 -> (no attribute)

synthesized: boost::fusion::deque<int, int>
bound:       boost::fusion::deque<int, int>
 -> (123 234)

synthesized: boost::fusion::deque<int, int>
bound:       std::pair<int, int>
 -> (123 234)

synthesized: boost::fusion::deque<int, int>
bound:       std::tuple<int, int>
 -> (123 234)
Run Code Online (Sandbox Code Playgroud)

请注意,这里的操作是完全多余的,只是用于添加输出:

住在科里鲁

template <typename T = x3::unused_type> void test() {
    static constexpr std::string_view input = "123 234";

    T attr;
    phrase_parse(begin(input), end(input), x3::int_ >> x3::int_, x3::space, attr);

    if constexpr (std::is_same_v<x3::unused_type, T>)
        std::cout << " -> (no attribute)\n";
    else
        std::cout << " -> " << boost::fusion::as_vector(attr) << "\n";
}

int main() {
    test();
    test<boost::fusion::deque<int, int>>();
    test<std::pair<int, int>>();
    test<std::tuple<int, int>>();
}
Run Code Online (Sandbox Code Playgroud)

仍在打印

 -> (no attribute)
 -> (123 234)
 -> (123 234)
 -> (123 234)
Run Code Online (Sandbox Code Playgroud)