我正在尝试使用boost::spirit:qi
从其他参数动态构建解析器。目标是解析字符串并使用键和值填充std::map<std::string, std::string>
。但是,不解析映射的关键字段(即,在生成解析器之前已知它)。
我猜我需要编写一个语义操作,将地图的键设置为适当的解析值。我可以看到qi::_1
提供了解析器的内容,但是如何引用返回结构(在本例中为std::map
)?
如果std::map
在范围内,我可以直接指定它:
parser = lit(prefix) >> value_parser[map_[key] = _1];
但在我的情况下,我想实际生成一个解析器,而不是解析。我猜我需要替换map_[key]
。
提供更多上下文(根据要求):
我首先解析一个看起来像这样的“模板”字符串:
/path/to/:somewhere:/nifty.json
:somewhere:
用于表示以后可以通过名称somewhere
引用的任何字符串。我的解析器运行良好。
接下来我想从该模板生成另一个解析字符串的解析器:
/path/to/anywhere/nifty.json
并向我提供std::map<std::string, std::string> m
m["somewhere"] == "anywhere"
。
答案 0 :(得分:3)
我不确定这是否是您的想法,但继承的属性可能是您的答案。您可以创建一个解析器,而不是动态创建解析器,而是将该键和对地图的引用作为您在每次调用时提供的继承属性:
// an attempt to demonstrate a parser that takes a std::map by reference and a key by value,
// then stores a parsed value into the map as the value associated with the given key
#include <string>
#include <map>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
typedef std::string::const_iterator fwd_iter_t;
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
typedef int value_t; // or whatever
typedef std::map<std::string, value_t> result_map_t;
// key insight - rules can take "inherited" attributes (parameters in 2nd argument):
typedef qi::rule<fwd_iter_t,
void(result_map_t&, std::string), // inherit map ref and key to use
boost::spirit::ascii::space_type> map_insert_rule_t;
int main() {
result_map_t result_map;
std::vector<std::string> keys = { "A", "B", "C" };
std::string test_data = "PREFIX 1\nPREFIX 2\nPREFIX 3";
using boost::phoenix::construct; // to create pairs
using boost::phoenix::insert; // to add pairs to the map
typedef result_map_t::value_type result_map_pair_t;
// use Phoenix actions to construct the key/value pair and insert it
map_insert_rule_t maprule = qi::lit("PREFIX")
>> qi::int_[insert(qi::_r1, // inherited map ref
construct<result_map_pair_t>(qi::_r2, qi::_1))];
fwd_iter_t beg = test_data.begin();
fwd_iter_t end = test_data.end();
for (auto k_it = keys.begin(); k_it != keys.end(); ++k_it) {
using boost::spirit::ascii::space;
if (!qi::phrase_parse(beg, end,
maprule(phoenix::ref(result_map), *k_it),
space)) {
std::cerr << "parse failed!" << std::endl;
return 1;
}
}
std::cout << "parse results:" << std::endl;
for (auto r_it = result_map.begin(); r_it != result_map.end(); ++r_it) {
std::cout << r_it->first << " " << r_it->second << std::endl;
}
return 0;
}
您可以通过继承qi :: rule并使其成为私有数据成员来消除调用中的std :: map引用。
答案 1 :(得分:1)
你应该能够使用phoenix bind做你所要求的事情,但似乎如果我们有更多的背景,可能会提供更清晰的解决方案。
parser = lit(prefix) >> value_parser[phx::ref(map)[key] = qi::_1]
根据密钥的来源,您可能还需要使用phx :: ref。