Boost.SSpirit.x3避免将同一类型的两个连续属性塌陷为向量

Boost.Spirit.x3 avoid collapsing two consecutive attributes of the same type into a vector

本文关键字:连续 两个 属性 向量 x3 SSpirit 类型 Boost      更新时间:2023-10-16

我正在努力学习BoostSpirit,但我发现了一个困难。

我正试图将一个字符串解析为以下结构:

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

看起来,当两个具有相同类型的属性背靠背时,它们(逻辑上)会塌陷为该类型的std::vector。由于该规则,以下语法分析器

+x3::ascii::alnum >>
    +x3::space >>
    +x3::ascii::alnum

将具有CCD_ 2的属性。

但我正试图将其解析为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;
}

实时查看

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

所以问题是:有没有办法(希望)将相同类型的两个连续属性从std::vector中干净地拆分为boost::fusion::vector

提前感谢您走到这一步;)。

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

omit指令就是你想要的,通过这一次添加,你的"无效代码"就可以工作了。:-]

// 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';
}

在线演示