如何解决在提升精神中使用语义动作来设置字段
假设您有一个使用 boost 精神的解析器,该解析器设置了该字段,但 id 字段除外。是否可以使用语义操作来生成和设置 id 字段?或者有没有更好的方法来实现这一点。
struct employee
{
std::string id;
int age;
std::string surname;
std::string forename;
double salary;
};
BOOST_FUSION_ADAPT_STRUCT(
client::employee,(int,age)
(std::string,id)
(std::string,surname)
(std::string,forename)
(double,salary)
)
解决方法
是的,这是可能的。
一个陷阱是语义动作的存在通常会抑制自动属性传播。由于您希望同时拥有两者,您需要使用 %=
而不是 =
(请参阅 docs)来分配解析器表达式。
或者,您可以动态生成一个值并使用您展示的改编。
概念验证:SA + 改编
在这里,我只是从改编中排除 id
。另请注意,自 c++11 以来您不需要重复类型:
BOOST_FUSION_ADAPT_STRUCT(
client::employee,age,/*id,*/ surname,forename,salary)
我更喜欢用一些 phoenix 函数助手编写 SA:
auto _id = px::function { std::mem_fn(&client::employee::id) };
auto _gen = px::function { client::employee::generate_id };
start %= skip(space) [
age >> name >> name >> salary >> eps
[ _id(_val) = _gen() ]
];
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iomanip>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
namespace fus = boost::fusion;
namespace client {
struct employee {
std::string id;
int age;
std::string surname;
std::string forename;
double salary;
static std::string generate_id() {
static int _next{0};
return "autoid#" + std::to_string(_next++);
}
};
using fus::operator<<;
}
BOOST_FUSION_ADAPT_STRUCT(
client::employee,salary)
template <typename It>
struct Parser : qi::grammar<It,client::employee()> {
Parser() : Parser::base_type(start) {
using namespace qi;
name = +graph;
age = uint_ >> eps(_val < 110 && _val > 16);
salary = double_;
auto _id = px::function { std::mem_fn(&client::employee::id) };
auto _gen = px::function { client::employee::generate_id };
start %= skip(space) [
age >> name >> name >> salary >> eps
[ _id(_val) = _gen() ]
];
BOOST_SPIRIT_DEBUG_NODES((start)(name)(age)(salary))
}
private:
qi::rule<It,client::employee()> start;
qi::rule<It,unsigned()> age;
qi::rule<It,std::string()> name;
qi::rule<It,double()> salary;
};
static auto qview(auto f,auto l) {
return std::quoted(
std::string_view(std::addressof(*f),std::distance(f,l)));
}
int main() {
Parser<std::string::const_iterator> p;
std::cout << fus::tuple_delimiter(',');
for (std::string const& input: {
//age surname forename salary
"55 Astley Rick 7232.88","23 Black Rebecca 0.00","77 Waters Roger 24815.07",})
{
auto f = begin(input),l = end(input);
client::employee emp;
if (parse(f,l,p,emp)) {
std::cout << "Parsed: " << emp.id << " " << emp << "\n";
} else {
std::cout << "Parse failed\n";
}
if (f != l) {
std::cout << "Remaining unput: " << qview(f,l) << "\n";
}
}
}
印刷品
Parsed: autoid#0 (55,Astley,Rick,7232.88)
Parsed: autoid#1 (23,Black,Rebecca,0)
Parsed: autoid#2 (77,Waters,Roger,24815.1)
替代方案:内联生成
你会保持完全适应:
BOOST_FUSION_ADAPT_STRUCT(
client::employee,id,surname,salary)
并在正确的位置使用 qi::attr()
重新拼写规则:
auto _gen = px::function { client::employee::generate_id };
start %= skip(space) [
age >> attr(_gen()) >> name >> name >> salary
];
Live On Coliru(省略未更改列表的其余部分)
打印(再次):
Parsed: autoid#0 (55,autoid#0,autoid#1,autoid#2,24815.1)
结论
回想起来,我认为替代方案更具吸引力。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。