微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在提升精神中使用语义动作来设置字段

如何解决在提升精神中使用语义动作来设置字段

假设您有一个使用 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() ]
    ];

Live On Coliru

#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 举报,一经查实,本站将立刻删除。