Boost X3:可以在析取中避免变体成员吗?

Boost X3: Can a variant member be avoided in disjunctions?

我想解析 string | (string, int) 并将其存储在将 int 组件默认为某个值的结构中。 X3中这样一个构造的属性是一个variant<string, tuple<string, int>>。我在想我可以有一个 struct,它需要一个 string 或一个 (string, int) 来自动填充:

    struct bar
    {
        bar (std::string x = "", int y = 0) : baz1 {x}, baz2 {y} {}

        std::string          baz1;
        int                  baz2;
    };

BOOST_FUSION_ADAPT_STRUCT (disj::ast::bar, baz1, baz2)

然后只需:

    const x3::rule<class bar, ast::bar> bar = "bar";
    using x3::int_;
    using x3::ascii::alnum;

    auto const bar_def = (+(alnum) | ('(' >> +(alnum) >> ',' >> int_ >> ')')) >> ';';

    BOOST_SPIRIT_DEFINE(bar);

但是这不起作用:

/usr/include/boost/spirit/home/x3/core/detail/parse_into_container.hpp:139:59: error: static assertion failed: Expecting a single element fusion sequence
  139 |             static_assert(traits::has_size<Attribute, 1>::value,

baz2 设置为 optional 没有帮助。解决此问题的一种方法是拥有一个 variant 字段或从该类型继承:

    struct string_int {
        std::string s;
        int i;
    };

    struct foo {
        boost::variant<std::string, string_int>  var;
    };

BOOST_FUSION_ADAPT_STRUCT (disj::ast::string_int, s, i)
BOOST_FUSION_ADAPT_STRUCT (disj::ast::foo, var)

(出于某种原因,我必须使用 boost::variant 而不是 x3::variant 才能使 operator<< 工作;另外,使用 std::pairtuple string_int 不起作用,但 boost::fusion::deque 起作用。)然后可以以某种方式装备 foo 以获取字符串和整数。

问题:在 X3 中执行此操作的正确、干净的方法是什么?有没有比第二个选项更自然的方法并为 foo 配备访问器?

Live On Coliru

遗憾的是 x3 section is exceedingly sparse and allows it (contrast the Qi section) 中的措辞。快速测试证实了这一点:

Live On Coliru

#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;

template <typename Expr>
std::string inspect(Expr const& expr) {
    using A = typename x3::traits::attribute_of<Expr, x3::unused_type>::type;
    return boost::core::demangle(typeid(A).name());
}

int main()
{
    std::cout << inspect(x3::double_ | x3::int_) << "\n";       // variant expected
    std::cout << inspect(x3::int_ | "bla" >> x3::int_) << "\n"; // variant "understandable"
    std::cout << inspect(x3::int_ | x3::int_) << "\n";          // variant suprising:
}

版画

boost::variant<double, int>
boost::variant<int, int>
boost::variant<int, int>

所有的希望都没有落空

在您的特定情况下,您可以欺骗系统:

auto const bar_def =                              //
    (+x3::alnum >> x3::attr(-1)                   //
    | '(' >> +x3::alnum >> ',' >> x3::int_ >> ')' //
    ) >> ';';

请注意我们如何为第一个分支“注入”一个 int 值。即满足属性传播大神:

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <iomanip>
namespace x3 = boost::spirit::x3;

namespace disj::ast {
    struct bar {
        std::string x;
        int         y;
    };
    using boost::fusion::operator<<;
} // namespace disj::ast

BOOST_FUSION_ADAPT_STRUCT(disj::ast::bar, x, y)

namespace disj::parser {
    const x3::rule<class bar, ast::bar> bar = "bar";

    auto const bar_def =                              //
        (+x3::alnum >> x3::attr(-1)                   //
        | '(' >> +x3::alnum >> ',' >> x3::int_ >> ')' //
        ) >> ';';

    BOOST_SPIRIT_DEFINE(bar)
}

namespace disj {
    void run_tests() {
        for (std::string const input : {
                 "",
                 ";",
                 "bla;",
                 "bla, 42;",
                 "(bla, 42);",
             }) {

            ast::bar val;
            auto f = begin(input), l = end(input);

            std::cout << "\n" << quoted(input) << " -> ";

            if (phrase_parse(f, l, parser::bar, x3::space, val)) {
                std::cout << "Parsed: " << val << "\n";
            } else {
                std::cout << "Failed\n";
            }

            if (f!=l) {
                std::cout << " -- Remaining " << quoted(std::string_view(f, l)) << "\n";
            }
        }
    }
}

int main()
{
    disj::run_tests();
}

版画

"" -> Failed

";" -> Failed
 -- Remaining ";"

"bla;" -> Parsed: (bla -1)

"bla, 42;" -> Failed
 -- Remaining "bla, 42;"

"(bla, 42);" -> Parsed: (bla 42)

¹ 只是 today