感谢任何帮助。我正在编写一个解析器,它可以与 GCC 4.9.3 + Boost 1_55 及更早版本一起使用,但不能与 Boost(或 GCC)的更高版本一起使用。
错误是:
错误:与“operator->”不匹配(操作数类型为“const _r1_type {aka const boost::phoenix::actorboost::spirit::attribute<1 >}”和“test::my_config::value_map_t {aka std:” :mapstd::__cxx11::basic_string
代码是:
one_statement = ( key > lit( '=' ) > qi::int_
> lit( ';' ) )
[ if_( phoenix::bind( &value_map_t::count, _r1->* &my_config::values, _1 ) )
phoenix::bind( &insert_value, _r1, _1, _2 )
];
typedef boost::variant<
std::string,
double,
int,
bool,
std::vector<double>
> parsed_types;
typedef value_map_t is std::string<string, parsed_types>;
value_map_t values;
qi::rule<Iterator, std::pair<std::string, config_section::parsed_types>( my_config* ), skipper_t> one_statement;
这用于按预期进行编译和解析。
自 c++14 起,
std::map::count
是一个重载函数:https://en.cppreference.com/w/cpp/container/map/count
这就是为什么c++20不允许程序获取标准库函数的地址(参见https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0551r3.pdf 和 我可以获取标准库中定义的函数的地址吗?)。当然,很多人都会这样做,但正如您所发现的,它会默默地破坏代码。
在你去看之前,新的c++20
没有同样的问题https://en.cppreference.com/w/cpp/container/map/containsstd::map::contains
考虑到很少的背景,我建议进行一些简化。
其一,您希望用一条规则解析所有值类型。其次,您不需要语义操作。如果您坚持,只需检查该密钥是否已存在于
insert_value
辅助函数的一部分即可:
static bool insert_value(my_config* cfg, std::string key, config_section::parsed_types val) {
if (!cfg)
return false;
return cfg->values.emplace(std::move(key), std::move(val)).second; // makes contains(key) check redundant
}
仅当值实际插入时此函数才会返回 true。规则可以变成:
one_statement = (key_ > '=' > value_ > ';')[px::bind(&insert_value, _r1, _1, _2)];
以及调用
count
或 contains
的整个丑陋混乱。
#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
namespace config_section {
using parsed_types = boost::variant<std::string, double, int, bool, std::vector<double>>;
} // namespace config_section
using value_map_t = std::map<std::string, config_section::parsed_types>;
struct my_config {
value_map_t values;
};
static bool insert_value(my_config* cfg, std::string key, config_section::parsed_types val) {
if (!cfg)
return false;
return cfg->values.emplace(std::move(key), std::move(val)).second; // makes contains(key) check redundant
}
namespace std { // hack for debug output
static inline std::ostream& operator<<(std::ostream& os, std::vector<double> const& dd) {
os << "{";
for (auto sep = " "; auto d : dd)
os << std::exchange(sep, ", ") << d;
return os << " }";
}
} // namespace std
int main() {
using Iterator = std::string::const_iterator;
using namespace qi::labels;
using skipper_t = qi::blank_type;
qi::rule<Iterator, std::string()> key_, string_;
qi::rule<Iterator, config_section::parsed_types(), skipper_t> value_;
qi::rule<Iterator, std::vector<double>(), skipper_t> vec_;
qi::rule<Iterator, void(my_config*), skipper_t> one_statement;
qi::real_parser<double, qi::strict_real_policies<double>> strict_double_{};
key_ = qi::alpha >> +qi::char_("_a-zA-Z0-9");
vec_ = '{' >> -(qi::double_ % ',') >> '}';
string_ = '"' >> *('\\' >> qi::char_ | ~qi::char_('"')) >> '"';
value_ = string_ | vec_ | qi::no_case[qi::bool_] | strict_double_ | qi::int_;
one_statement = (key_ > '=' > value_ > ';')[px::bind(&insert_value, _r1, _1, _2)];
my_config cfg;
for (std::string const input :
{
R"(simple_int = 42;)",
R"(simple_true = true; )",
R"(simple_false = False;)",
R"(simple_string = "Hello World!"; )",
R"(escaped_string = "Hello \"Jolly\" World!"; )",
R"(simple_double = 42.;)",
R"(another_double = -inf;)",
R"(more_double = 4.2e3;)",
R"(empty_vec = {};)",
R"(filled_vec = { 123, 234, -4e9, +Inf, NaN, 8e-2, +0 };)",
}) //
{
auto f = begin(input), l = end(input);
std::cout << " ===== parsed: " << std::boolalpha << phrase_parse(f, l, one_statement(&cfg), qi::blank)
<< "\n";
if (f != l)
std::cout << " remaining unparsed: " << quoted(std::string(f, l)) << "\n";
}
for (auto& [k, v] : cfg.values) {
std::cout << quoted(k) << "\t -> " << v << "\n";
}
}
印刷
===== parsed: true
===== parsed: true
===== parsed: true
===== parsed: true
===== parsed: true
===== parsed: true
===== parsed: true
===== parsed: true
===== parsed: true
===== parsed: true
"another_double" -> -inf
"empty_vec" -> { }
"escaped_string" -> Hello "Jolly" World!
"filled_vec" -> { 123, 234, -4e+09, inf, nan, 0.08, 0 }
"more_double" -> 4200
"simple_double" -> 42
"simple_false" -> false
"simple_int" -> 42
"simple_string" -> Hello World!
"simple_true" -> true
就像我说的,你不需要这些。这是因为
insert
也不会覆盖现有值。因此,进一步简化:
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <map>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
namespace config_section {
using parsed_types = boost::variant<std::string, double, int, bool, std::vector<double>>;
} // namespace config_section
using value_map_t = std::map<std::string, config_section::parsed_types>;
struct my_config {
value_map_t values;
};
namespace std { // hack for debug output
static inline std::ostream& operator<<(std::ostream& os, std::vector<double> const& dd) {
os << "{";
for (auto sep = " "; auto d : dd)
os << std::exchange(sep, ", ") << d;
return os << " }";
}
} // namespace std
template <typename Iterator> struct ConfigSection : qi::grammar<Iterator, value_map_t()> {
ConfigSection() : ConfigSection::base_type(start) {
key_ = qi::alpha >> +qi::char_("_a-zA-Z0-9");
vec_ = '{' >> -(qi::double_ % ',') >> '}';
string_ = '"' >> *('\\' >> qi::char_ | ~qi::char_('"')) >> '"';
value_ = string_ | vec_ | qi::no_case[qi::bool_] | strict_double_ | qi::int_;
one_statement = key_ > '=' > value_ > ';';
config_ = *one_statement;
start = qi::skip(qi::space)[config_ > qi::eoi];
}
private:
qi::rule<Iterator, value_map_t()> start;
using Entry = std::pair<std::string, config_section::parsed_types>;
using skipper_t = qi::space_type;
qi::rule<Iterator, value_map_t(), skipper_t> config_;
qi::rule<Iterator, config_section::parsed_types(), skipper_t> value_;
qi::rule<Iterator, std::vector<double>(), skipper_t> vec_;
qi::rule<Iterator, Entry(), skipper_t> one_statement;
// lexemes
qi::real_parser<double, qi::strict_real_policies<double>> strict_double_;
qi::rule<Iterator, std::string()> key_, string_;
};
int main() {
static ConfigSection<std::string::const_iterator> const parser;
std::string const input = R"(
simple_int = 42;
simple_true = true;
simple_false = False;
simple_string = "Hello World!";
escaped_string = "Hello \"Jolly\" World!";
simple_double = 42.;
another_double = -inf;
more_double = 4.2e3;
empty_vec = {};
filled_vec = { 123, 234, -4e9, +Inf, NaN, 8e-2, +0 };
simple_int = "WILL IT BLEND?"; )"; // simple_int not overwritten
my_config cfg;
std::cout << " ===== parsed: " << std::boolalpha //
<< parse(begin(input), end(input), parser, cfg.values) << "\n";
for (auto& [k, v] : cfg.values)
std::cout << quoted(k) << "\t -> " << v << "\n";
}
印刷
===== parsed: true
"another_double" -> -inf
"empty_vec" -> { }
"escaped_string" -> Hello "Jolly" World!
"filled_vec" -> { 123, 234, -4e+09, inf, nan, 0.08, 0 }
"more_double" -> 4200
"simple_double" -> 42
"simple_false" -> false
"simple_int" -> 42
"simple_string" -> Hello World!
"simple_true" -> true