下面的程序是一个人工示例(从我正在研究的一个更大的语法中缩减而来),以展示一种奇怪的行为。
程序按原样运行的输出是"hello",是不正确的。
如果我从quoted_string
规则中删除(在本例中无用)语义操作,输出是预期的"foo=hello"。
#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <vector>
#include <string>
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include "utils.hpp"
namespace t {
using std::vector;
using std::string;
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
template <typename Iterator, typename Skipper=qi::space_type>
struct G1 : qi::grammar<Iterator, string(), Skipper> {
template <typename T>
using rule = qi::rule<Iterator, T, Skipper>;
qi::rule<Iterator, string(), qi::locals<char>> quoted_string;
rule<string()> start;
G1() : G1::base_type(start, "G1") {
{
using qi::_1;
using qi::_a;
using attr_signature = vector<char>;
auto handler = [](attr_signature const& elements) -> string {
string output;
for(auto& e : elements) {
output += e;
}
return output;
};
quoted_string = (qi::omit[qi::char_("'"")[_a = _1]]
>> +(qi::char_ - qi::char_(_a))
>> qi::lit(_a))[qi::_val = phx::bind(handler, _1)];
}
start = qi::string("foo") >> -(qi::string("=") >> quoted_string);
}
};
string parse(string const input) {
G1<string::const_iterator> g;
string result;
phrase_parse(begin(input), end(input), g, qi::standard::space, result);
return result;
}
};
int main() {
using namespace std;
auto r = t::parse("foo='hello'");
cout << r << endl;
}
我肯定能找到一个解决方案,但我要弄清楚我错过了什么
就像@cv_and_he解释的那样,您正在用handler(_1)
的结果覆盖属性。由于属性是通过引用传递的,因此会丢失原始值。
自动属性传播规则知道如何连接"字符串"容器值,所以为什么不使用默认实现呢?
quoted_string %= qi::omit[qi::char_("'"")[_a = _1]]
>> +(qi::char_ - qi::char_(_a))
>> qi::lit(_a);
(注意 %=
;这允许自动传播(即使在存在语义操作的情况下)。
或者,您可以从SA内部推回:
>> +(qi::char_ - qi::char_(_a)) [ phx::push_back(qi::_val, _1) ]
并且,如果您确实需要在handler
中进行一些处理,则使其通过引用获取字符串:
auto handler = [](attr_signature const& elements, std::string& attr) {
for(auto& e : elements) {
attr += e;
}
};
quoted_string = (qi::omit[qi::char_("'"")[_a = _1]]
>> +(qi::char_ - qi::char_(_a))
>> qi::lit(_a)) [ phx::bind(handler, _1, qi::_val) ];
所有这些方法都有效。
对于真正繁重的事情,我过去使用自定义字符串类型与boost::spirit::traits自定义点来进行转换:
- http://www.boost.org/doc/libs/1_55_0/libs/spirit/doc/html/spirit/advanced/customize.html