gpt4 book ai didi

c++ - Boost.Qi 规则是否总是在使用它们的表达式中通过引用保存?

转载 作者:搜寻专家 更新时间:2023-10-31 01:33:51 27 4
gpt4 key购买 nike

假设我有一个简单的类型foo:

struct foo { void bar(int) { // do something } };

我想使用 Boost.Qi 来解析字符串中的整数字段,并使用结果值调用 foo::bar()。这可以通过语义操作来完成,如下所示:

std::string str;
bar b1, b2;
boost::spirit::phrase_parse(str.begin(), str.end(),
boost::spirit::int_[boost::bind(&foo::bar, &b1, _1)] >>
boost::spirit::int_[boost::bind(&foo::bar, &b2, _1)]);

这有很多重复,所以我可以像这样删除一些样板:

template <typename Iterator>
boost::spirit::qi::rule<Iterator, int(), boost::spirit::qi::space> parse_bar(bar *b)
{
return boost::spirit::int_[boost::bind(&foo::bar, _1)];
}

std::string str;
bar b1, b2;
boost::spirit::phrase_parse(str.begin(), str.end(),
parse_bar(&b1) >> parse_bar(b2));

这看起来更好而且有效。但是,有时我想保存解析器表达式供以后使用(例如,如果我想按值捕获它以便在 lambda 函数中延迟使用)。在原始示例中,我可以执行以下操作:

std::string str;
bar b1, b2;
auto parser = boost::proto::deep_copy(
boost::spirit::int_[boost::bind(&foo::bar, &b1, _1)] >>
boost::spirit::int_[boost::bind(&foo::bar, &b2, _1)]);
auto lambda = [parser]() { boost::spirit::phrase_parse(str.begin(), str.end(), parser); };

// some time later
lambda();

这也有效(尽管需要调用 boost::proto::deep_copy() 来确保 AST 中的任何内部节点都不是通过引用保存的)。我的第一印象是我可以应用相同的 rule 重构来将上面的代码简化为:

std::string str;
bar b1, b2;
auto parser = boost::proto::deep_copy(parse_bar(&b1) >> parse_bar(&b2));
auto lambda = [parser]() { boost::spirit::phrase_parse(str.begin(), str.end(), parser); };

// some time later
lambda();

但是,这会导致稍后调用 lambda() 时出现问题。根据我的调试,似乎 parse_bar 返回的 rule 对象总是保存在表达式中通过引用,即使在调用 深度复制()。由于 rule 对象是包含调用 deep_copy() 的行中的右值,因此在稍后调用 phrase_parse()< 时,对它们的引用无效.

这似乎表明 rule 对象始终是左值,其生命周期至少与引用它们的表达式的生命周期相匹配。这是真的?我想我可能误解了图书馆的一个关键概念,并试图以“错误的方式”做到这一点。在我的申请中,我没有正式的语法;我正在寻找一种简单、紧凑的方法来定义大量内联的类似解析器表达式,并使用调用绑定(bind)成员函数的语义操作,如上所示。

最佳答案

简单的回答:是的。

<!-- reads the rest of your question -->

您的代码示例中有很多不准确之处。

第一个片段

  1. bar b1, b2; // probably meant foo?
  2. boost::spirit::phrase_parse 不存在,boost::spirit::int_ 也不存在
  3. boost::bind 不能作为语义 Action
  4. phrase_parse 需要一个船长,你不提供

Fixed:

    std::string str = "123 234";
qi::phrase_parse(str.begin(), str.end(),
qi::int_[boost::bind(&foo::bar, &b1, _1)] >>
qi::int_[boost::bind(&foo::bar, &b2, _1)],
qi::space);

第二个片段

在随后的示例中,您有更多 barfoo 的混淆,您将 qi::space 作为类型参数传递,bind 无法绑定(bind)到 b 等等。无需重复上述操作并跳过明显的错误

  1. 存在一个基本问题,模板函数需要用作 parse_bar<std::string::const_iterator>。我会说它看起来不再“漂亮”了。
  2. 规则声明 int() 意味着您公开一个属性,但它从未被分配或使用

Fixed:

template <typename Iterator>
static qi::rule<Iterator, qi::space_type> parse_bar(foo *b) {
return qi::int_ [boost::bind(&foo::bar, b, _1)];
}

int main() {
foo b1, b2;
using It = std::string::const_iterator;

std::string const str = "234 345";
qi::phrase_parse(str.begin(), str.end(), parse_bar<It>(&b1) >> parse_bar<It>(&b2), qi::space);

第三个片段

大部分都是一样的问题,另外就是没有抓到str

第四个片段

是的。看我回答的第一行

解决方案

跳出框框思考。您需要将解析器绑定(bind)到带外属性的“可爱”语法。

注意

  • 这就像回到 Spirit V1(经典)设计。这不是图书馆的精神 [原文如此],可以这么说
  • 如果您只想这样做,使用继承的属性?

    std::string const str = "111 222";

    // use phoenix action
    auto foo_bar = std::mem_fn(&foo::bar);
    px::function<decltype(foo_bar)> foo_bar_(foo_bar);

    // with inherited attributes
    qi::rule<It, void(foo*)> _foo = qi::int_ [ foo_bar_(qi::_r1, qi::_1) ];

    auto lambda = [_foo,&b1,&b2,str] { qi::phrase_parse(str.begin(), str.end(), _foo(&b1) >> _foo(&b2), qi::space); };

    // some time later
    lambda();

    std::cout << b1 << ", " << b2 << "\n";
  • 如果您希望像 foo 这样对您的类型提供“神奇”支持,并且它在逻辑上类似于赋值/转换,请使用特征:http://www.boost.org/doc/libs/1_62_0/libs/spirit/doc/html/spirit/advanced/customize.html

现场演示

我总是喜欢添加一个现场演示,所以给你:

Live On Wandbox

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/bind.hpp>
#include <iostream>

namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;

struct foo {
int value = 42;
void bar(int i) { value = i; }
friend std::ostream& operator<<(std::ostream& os, foo const& f) { return os << "{" << f.value << "}"; }
};

template <typename Iterator>
static qi::rule<Iterator, qi::space_type> parse_bar(foo *b) {
return qi::int_ [boost::bind(&foo::bar, b, _1)];
}

int main() {
foo b1, b2;
using It = std::string::const_iterator;

// snippet 1
{
std::string str = "123 234";
qi::phrase_parse(str.begin(), str.end(),
qi::int_[boost::bind(&foo::bar, &b1, _1)] >>
qi::int_[boost::bind(&foo::bar, &b2, _1)],
qi::space);

std::cout << b1 << ", " << b2 << "\n";
}

// snippet 2
{
std::string const str = "234 345";
qi::phrase_parse(str.begin(), str.end(), parse_bar<It>(&b1) >> parse_bar<It>(&b2), qi::space);

std::cout << b1 << ", " << b2 << "\n";
}

// snippet 3
{
std::string const str = "345 456";

auto parser = boost::proto::deep_copy(
qi::int_[boost::bind(&foo::bar, &b1, _1)] >>
qi::int_[boost::bind(&foo::bar, &b2, _1)]);

auto lambda = [parser,str]() { qi::phrase_parse(str.begin(), str.end(), parser, qi::space); };

// some time later
lambda();

std::cout << b1 << ", " << b2 << "\n";
}

// snippet 4
{
std::string const str = "456 567";
auto parser = boost::proto::deep_copy(parse_bar<It>(&b1) >> parse_bar<It>(&b2));
auto lambda = [parser=qi::copy(parser), str]() { qi::phrase_parse(str.begin(), str.end(), parser, qi::space); };

// some time later
//lambda();
//// no workey
}

// SOLUTIONS
{
std::string const str = "111 222";

// use phoenix action
auto foo_bar = std::mem_fn(&foo::bar);
px::function<decltype(foo_bar)> foo_bar_(foo_bar);

// with inherited attributes
qi::rule<It, void(foo*)> _foo = qi::int_ [ foo_bar_(qi::_r1, qi::_1) ];

auto lambda = [_foo,&b1,&b2,str] { qi::phrase_parse(str.begin(), str.end(), _foo(&b1) >> _foo(&b2), qi::space); };

// some time later
lambda();

std::cout << b1 << ", " << b2 << "\n";
}
}

关于c++ - Boost.Qi 规则是否总是在使用它们的表达式中通过引用保存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40599490/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com