假设我有一个简单类型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()
时出现问题。根据我的调试,即使在调用rule
之后,parse_bar
返回的deep_copy()
对象似乎始终通过引用保存在表达式中。由于rule
对象是包含对deep_copy()
的调用的行中的右值,因此在稍后调用phrase_parse()
期间对它们的引用无效。
这似乎表明rule
个对象始终是lvalues,其生命周期至少与引用它们的表达式的生命周期相匹配。这是真的?我想我可能误解了图书馆的一个关键概念,并试图以“错误的方式”这样做。在我的申请中,我没有正式的语法;我正在寻找一种简单,紧凑的方法来内联定义大量类似的解析器表达式,并使用语义动作调用绑定的成员函数,如上所示。
答案 0 :(得分:3)
简单回答:是的。
<!-- reads the rest of your question -->
您的代码示例中存在许多不准确之处。
bar b1, b2; // probably meant foo?
boost::spirit::phrase_parse
不存在,boost::spirit::int_
boost::bind
不能作为语义动作phrase_parse
需要一名船长,您不提供修正:
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>
。我会说它看起来不再“好看”。int()
意味着您公开属性,但它从未被分配或使用修正:
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
之外,大多数问题都没有被捕获。
是。请参阅我的答案的第一行
在盒子外面思考。您需要“可爱”语法将解析器绑定到带外属性。
请注意
如果您只想这样,请使用继承的属性?
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";
}
}