Boost.Spirit.x3避免将相同类型的两个连续属性折叠到向量中

时间:2016-05-10 03:15:59

标签: c++ boost c++14 boost-spirit boost-spirit-x3

我正在努力学习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;
}

See it live

此代码触发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;
}

See it live

我真的不喜欢这个解决方案:它有点破坏了精神的惊人表现力并使它变得非常难看,如果我想在employee结构中添加新字段,那么我必须添加一个额外的lambda,而不仅仅是更新我的BOOST_FUSION_ADAPT_STRUCT,这更容易。

所以问题是:是否有某种方法(希望)将std::vectorboost::fusion::vector中相同类型的两个连续属性干净地分开?

提前感谢你们这么做;)。

1 个答案:

答案 0 :(得分:6)

问题在于,与字符文字不同,x3::space具有属性。因此,您没有由空格分隔的两个单独字符序列的属性,而是包含空格的一个大字符序列的属性。

The omit directive就是您所追求的,只需添加一个“不工作代码”即可。 : - ]

// 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