Boost.Qi 规则是否总是在使用它们的表达式中通过引用保存?
Are Boost.Qi rules always held by reference inside expressions that use them?
假设我有一个简单类型 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 中的 none 内部节点通过引用保存)。我的第一印象是我可以应用相同的 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
对象似乎总是通过引用 保存在表达式 中,即使在调用 [=23= 之后也是如此].由于 rule
对象在包含对 deep_copy()
的调用的行中是右值,因此在稍后对 phrase_parse()
.
的调用中对它们的引用无效
这似乎表明 rule
对象始终是左值,其生命周期至少与引用它们的表达式的生命周期相匹配。这是真的?我想我可能误解了库的一个关键概念,并且正在尝试这样做 "wrong way." 在我的应用程序中,我没有正式的语法;我正在寻找一种简单、紧凑的方法来定义大量内联的类似解析器表达式,并使用调用绑定成员函数的语义操作,如上所示。
简单回答:是的。
<!-- reads the rest of your question -->
您的代码示例中有很多不准确之处。
第一个片段
bar b1, b2; // probably meant foo?
boost::spirit::phrase_parse
不存在,boost::spirit::int_
也不存在
boost::bind
不能作为语义动作
phrase_parse
需要船长,你提供none
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);
第二个片段
在随后的示例中,您有更多 bar
和 foo
的混淆,您将 qi::space
作为类型参数传递,bind
无法绑定到 b
等等等等,不重复上面的内容,跳过明显的错误
- 存在模板函数需要作为
parse_bar<std::string::const_iterator>
使用的根本问题。我会说它不再看起来 "nice".
- 规则声明
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
没有被捕获。
第四个片段
是的。看我回答的第一行
解决方案
跳出框框思考。您需要 "cute" 语法来将解析器绑定到带外属性。
备注
- 这就像回到 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";
如果您想要 'magical' 支持您的类型,例如 foo
并且逻辑上类似于 assignment/conversion,请使用特征:http://www.boost.org/doc/libs/1_62_0/libs/spirit/doc/html/spirit/advanced/customize.html
现场演示
我总是喜欢添加一个现场演示,所以在这里:
#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";
}
}
假设我有一个简单类型 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 中的 none 内部节点通过引用保存)。我的第一印象是我可以应用相同的 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
对象似乎总是通过引用 保存在表达式 中,即使在调用 [=23= 之后也是如此].由于 rule
对象在包含对 deep_copy()
的调用的行中是右值,因此在稍后对 phrase_parse()
.
这似乎表明 rule
对象始终是左值,其生命周期至少与引用它们的表达式的生命周期相匹配。这是真的?我想我可能误解了库的一个关键概念,并且正在尝试这样做 "wrong way." 在我的应用程序中,我没有正式的语法;我正在寻找一种简单、紧凑的方法来定义大量内联的类似解析器表达式,并使用调用绑定成员函数的语义操作,如上所示。
简单回答:是的。
<!-- reads the rest of your question -->
您的代码示例中有很多不准确之处。
第一个片段
bar b1, b2; // probably meant foo?
boost::spirit::phrase_parse
不存在,boost::spirit::int_
也不存在
boost::bind
不能作为语义动作phrase_parse
需要船长,你提供none
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);
第二个片段
在随后的示例中,您有更多 bar
和 foo
的混淆,您将 qi::space
作为类型参数传递,bind
无法绑定到 b
等等等等,不重复上面的内容,跳过明显的错误
- 存在模板函数需要作为
parse_bar<std::string::const_iterator>
使用的根本问题。我会说它不再看起来 "nice". - 规则声明
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
没有被捕获。
第四个片段
是的。看我回答的第一行
解决方案
跳出框框思考。您需要 "cute" 语法来将解析器绑定到带外属性。
备注
- 这就像回到 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";
如果您想要 'magical' 支持您的类型,例如
foo
并且逻辑上类似于 assignment/conversion,请使用特征:http://www.boost.org/doc/libs/1_62_0/libs/spirit/doc/html/spirit/advanced/customize.html
现场演示
我总是喜欢添加一个现场演示,所以在这里:
#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";
}
}