天真的“凤凰”方式是
rule_assign = key >> value
[ phx::bind(keywords.add, _1, _2) ];
rule_remove = key
[ phx::bind(keywords.remove, _1) ];
// and voila: (BROKEN)
rule_replace = key >> value
[ phx::bind(keywords.remove, _1),
phx::bind(keywords.add, _1, _2)
];
后者不起作用,我相信由于第一个绑定返回一个operator,
自身重载的对象,并且它比phoenix
's更受欢迎operator,
。
我建议你写一个小助手来解决这个问题:
struct assign_symbol_f
{
assign_symbol_f(Symbols& sym) : sym(sym) {}
typedef bool result_type;
template<typename Key, typename Value>
bool operator()(Key const& key, Value const& value) const
{
bool replaced = (nullptr != sym.find(key));
sym.remove(key);
sym.add(key, value);
return replaced;
}
private:
Symbols& sym;
};
这透明地分配或重新分配symbols
树中的项目。按如下方式使用它:
rule_replace_or_add = key >> value
[ phx::bind(assign_symbol_f(keywords), qi::_1, qi::_2) ];
现在,您可以拆分内容并更具体:
assign_symbol_f assign_sym(keywords);
rule_assign = key >> value
[ qi::_pass = !phx::bind(assign_sym, _1, _2) ];
rule_replace = key >> value
[ qi::_pass = phx::bind(assign_sym, _1, _2) ];
奖金
作为奖励,您可以通过为您的函子创建一个惰性actor来获得一些语法糖:
phx::function<assign_symbol_f> assign_sym;
// use it like
rule_assign = key >> value
[ qi::_pass = assign_sym(_1, _2) ];
rule_replace = key >> value
[ qi::_pass = assign_sym(_1, _2) ];
看妈!没有了phx::bind
。
完整演示
完成一个基本的测试套件:)
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
typedef qi::symbols<char, int> Symbols;
struct assign_symbol_f
{
assign_symbol_f(Symbols& sym) : sym(sym) {}
typedef bool result_type;
template<typename Key, typename Value>
bool operator()(Key const& key, Value const& value) const
{
bool replaced = (nullptr != sym.find(key));
sym.remove(key);
sym.add(key, value);
return replaced;
}
private:
Symbols& sym;
};
template <typename Iter> struct parser : qi::grammar<Iter, qi::space_type>
{
parser(Symbols &dict)
: parser::base_type(start),
assign_sym(dict)
{
using namespace qi;
identifier = +graph;
add_rule = lit("+") >> (identifier >> int_)
[ assign_sym(_1, _2) ]
;
del_rule = lit("-") >> identifier
[ phx::bind(dict.remove, _1) ]
;
start = (del_rule | add_rule) % ";";
}
private:
phx::function<assign_symbol_f> assign_sym;
qi::rule<Iter, qi::space_type> start, del_rule, add_rule;
qi::rule<Iter, std::string()> identifier; // no skipper
};
bool execute(std::string const& test, Symbols& dict)
{
auto f = test.begin(), l = test.end();
parser<std::string::const_iterator> prsr(dict);
return
qi::phrase_parse(f, l, prsr, qi::space)
&& (f == l);
}
int main() {
Symbols dict;
assert(execute("+foo 3; +bar 4; -foo", dict));
assert(!dict.find("foo"));
assert( dict.find("bar") && (4 == dict.at("bar")));
assert(!dict.find("zap"));
assert(execute("+zap -42; +bar 5; +foo 33", dict));
assert( dict.find("zap") && (-42 == dict.at("zap")));
assert( dict.find("bar") && (5 == dict.at("bar"))); // replaced
assert( dict.find("foo") && (33 == dict.at("foo")));
}