避免将两个连续的相同类型属性合并为向量的 Boost.Spirit.x3

9
我是一名学习Boost.Spirit的初学者,但我遇到了困难。
我正在尝试将一个字符串解析成以下结构:
struct employee {
    std::string name;
    std::string location;
};

当两个相同类型的属性挨在一起时,它们会逻辑上合并成一个该类型的std::vector。由于这个规则,下面的解析器...
+x3::ascii::alnum >>
    +x3::space >>
    +x3::ascii::alnum

这个属性应该是 std::vector<std::string>

但我正在尝试将其解析为struct,这意味着对我来说理想的属性应该是boost::fusion::tuple<std::string, std::string>,这样我就可以将我的结构体适应它。

不起作用的代码的完整版本(如上所述):

// Example program
#include <iostream>
#include <string>

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>


struct employee {
    std::string name;
    std::string location;
};

BOOST_FUSION_ADAPT_STRUCT(employee, 
    (std::string, name),
    (std::string, location)
)

namespace x3 = boost::spirit::x3;

x3::rule<struct parse_emp_id, employee> const parse_emp = "Employee Parser";
auto parse_emp_def = 
    +x3::ascii::alnum >>
    +x3::space >>
    +x3::ascii::alnum
    ;
BOOST_SPIRIT_DEFINE(parse_emp);

int main()
{
    std::string input = "Joe Fairbanks";
    
    employee ret;
    
    x3::parse(input.begin(), input.end(), parse_emp, ret);
    
    std::cout << "Name: " << ret.name << "\tLocation: " << ret.location << std::endl;
}

查看实时演示

这段代码会触发一个static_assert,告诉我我的属性不正确:

error: static_assert failed "Attribute does not have the expected size."

使用命令:
clang++ -std=c++14 test.cpp

(在GCC下也失败了)。

我尝试了什么

我已经找到了一个解决这个问题的方法,但它很杂乱,我不相信这是最干净的方式:

// Example program
#include <iostream>
#include <string>

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>


struct employee {
    std::string name;
    std::string location;
};

namespace x3 = boost::spirit::x3;

x3::rule<struct parse_emp_id, employee> const parse_emp = "Employee Parser";
auto parse_emp_def = 
    x3::eps [
    ([](auto& ctx) {
        x3::_val(ctx) = employee{};
    })
    ]>>
    (+x3::ascii::alnum)[
    ([](auto& ctx) {
        x3::_val(ctx).name = x3::_attr(ctx);
    })
    ]>>
    +x3::space >>
    (+x3::ascii::alnum)[
    ([](auto& ctx) {
        x3::_val(ctx).location = x3::_attr(ctx);
    })
    ]
    ;
BOOST_SPIRIT_DEFINE(parse_emp);

int main()
{
    std::string input = "Joe Fairbanks";
    
    employee ret;
    
    x3::parse(input.begin(), input.end(), parse_emp, ret);
    
    std::cout << "Name: " << ret.name << "\tLocation: " << ret.location << std::endl;
}

查看实时演示

我真的不喜欢那个解决方案:它有点破坏了 Spirit 的惊人表现力,并使其变得非常丑陋,而且如果我想将新字段添加到 employee 结构中,则必须添加额外的 lambda,而不是仅更新我的 BOOST_FUSION_ADAPT_STRUCT,这要容易得多。

因此问题是:是否有一种方法可以(希望)将同一类型的两个连续属性从 std::vector 中清晰地拆分并放入 boost::fusion::vector 中?

感谢您预先阅读;)。

1个回答

6
问题在于,与字符字面值不同,x3::space具有属性。因此,您没有两个由空格分隔的单独字符序列的属性,而是一个包括空格的大字符序列的属性。 忽略指令就是你想要的,只需添加这个指令,你的“不工作的代码”就会生效。 :-]
// Example program
#include <string>
#include <iostream>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/x3.hpp>

namespace x3 = boost::spirit::x3;

struct employee {
    std::string name;
    std::string location;
};
BOOST_FUSION_ADAPT_STRUCT(employee, name, location)

x3::rule<struct parse_emp_id, employee> const parse_emp = "Employee Parser";
auto parse_emp_def
    =      +x3::ascii::alnum
        >>  x3::omit[+x3::space]
        >> +x3::ascii::alnum
    ;
BOOST_SPIRIT_DEFINE(parse_emp)

int main()
{
    std::string const input = "Joe Fairbanks";

    employee ret;
    x3::parse(input.begin(), input.end(), parse_emp, ret);

    std::cout << "Name: " << ret.name << "\tLocation: " << ret.location << '\n';
}

Online Demo


3
当我发现问题之前,对于一些心灵问题已经有了答案,这是个好日子 :) +1 - sehe
@sehe:哈哈!这是我第一次在X3问题上赶在你前面,但是,这个问题非常简单。;-] - ildjarn
2
@Russel 我建议使用跳过器而不是显式的空格(请参见https://dev59.com/KXPYa4cB1Zd3GeqPgj4T#17073965)。[在Coliru上实时查看](http://coliru.stacked-crooked.com/a/444dad3ed2859a35)。 - sehe
1
我期望 omit 指令只是从字符序列中删除空格,而不是将其分成两个部分。为什么这样可以行呢? - Russell Greene
2
@RussellGreene: omit特别是没有属性,这是关键。+x3::ascii::alnum >> +' ' >> +x3::ascii::alnum也可以工作,无需使用omit,因为字符字面量也没有属性。 - ildjarn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接