C++ boost::spirit::qi::语法和可变模板
我在用可变模板定义语法时遇到了一个问题 我首先定义了一些包含在某些结构(例如纬度、经度)中的简单语法,如下所示:C++ boost::spirit::qi::语法和可变模板,c++,parsing,variadic-templates,boost-spirit-qi,C++,Parsing,Variadic Templates,Boost Spirit Qi,我在用可变模板定义语法时遇到了一个问题 我首先定义了一些包含在某些结构(例如纬度、经度)中的简单语法,如下所示: #include <boost/fusion/include/adapt_struct.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/variant.hpp> #include <iostream> #include <string> using n
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <iostream>
#include <string>
using namespace boost::spirit;
template <class Attribute>
using command_rule =
qi::rule<std::string::iterator, Attribute, ascii::space_type>;
template <class Attribute>
using command_grammar =
qi::grammar<std::string::iterator, Attribute, ascii::space_type>;
struct Latitude {
struct return_type {
double lat_;
};
struct grammar : command_grammar<return_type()> {
grammar() : grammar::base_type{latitude_} {
latitude_ = "LAT=" >> qi::double_;
}
private:
command_rule<return_type()> latitude_;
};
};
BOOST_FUSION_ADAPT_STRUCT(Latitude::return_type, (double, lat_))
struct Longitude {
struct return_type {
double lon_;
};
struct grammar : command_grammar<return_type()> {
grammar() : grammar::base_type{longitude_} {
longitude_ = "LON=" >> qi::double_;
}
private:
command_rule<return_type()> longitude_;
};
};
BOOST_FUSION_ADAPT_STRUCT(Longitude::return_type, (double, lon_))
语法对象似乎不能是临时的,必须存储为类的成员。我该怎么做
编辑:
我也曾尝试将此类对象存储在
std::tuple
中,但它似乎仍然不起作用。您正在创建的内容与qi的自动解析器的功能非常相似:
如果您专门针对您的数据类型,您可以直接使用qi::auto
:
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace Commands {
namespace qi = boost::spirit::qi;
template <class T> using Rule = qi::rule<std::string::const_iterator, T()>;
template <typename... T>
auto parse(std::string const& s) {
boost::variant<T...> v;
auto it = s.begin();
if (qi::parse(it, s.end(), qi::auto_, v))
return v;
throw std::runtime_error(std::string(__FUNCTION__) + " failed");
}
}
struct Latitude { double lat_; };
BOOST_FUSION_ADAPT_STRUCT(Latitude, lat_)
struct Longitude { double lon_; };
BOOST_FUSION_ADAPT_STRUCT(Longitude, lon_)
namespace boost { namespace spirit { namespace traits {
template <> struct create_parser<Latitude> {
using type = Commands::Rule<Latitude>;
static type const& call() {
static type const s_rule = qi::skip(qi::space)["LAT=" >> qi::auto_];
return s_rule;
};
};
template <> struct create_parser<Longitude> {
using type = Commands::Rule<Longitude>;
static type const& call() {
static type const s_rule = qi::skip(qi::space)["LON=" >> qi::auto_];
return s_rule;
};
};
} } }
struct print {
using result_type = void;
void operator()(Latitude const &t) const { std::cout << "Latitude = " << t.lat_ << " deg" << std::endl; }
void operator()(Longitude const &t) const { std::cout << "Longitude = " << t.lon_ << " deg" << std::endl; }
};
#include <sstream>
int main() {
std::istringstream iss("LAT=4.3\n LON=5.0");
std::string s;
print printer;
while (std::getline(iss, s)) try {
auto v = Commands::parse<Latitude, Longitude>(s);
boost::apply_visitor(printer, v);
}
catch (std::exception const& e) {
std::cout << "'" << s << "': " << e.what() << "\n";
}
}
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace Commands {
template <typename... T>
auto parse(std::string const& s) {
boost::variant<T...> v;
auto it = s.begin();
if (boost::spirit::qi::parse(it, s.end(), boost::spirit::qi::auto_, v))
return v;
throw std::runtime_error(std::string(__FUNCTION__) + " failed");
}
struct Latitude { double lat_; };
struct Longitude { double lon_; };
static inline std::ostream& operator<<(std::ostream& os, Latitude const &t) { return os << "Latitude = " << t.lat_ << " deg"; }
static inline std::ostream& operator<<(std::ostream& os, Longitude const &t) { return os << "Longitude = " << t.lon_ << " deg"; }
}
BOOST_FUSION_ADAPT_STRUCT(Commands::Latitude, lat_)
BOOST_FUSION_ADAPT_STRUCT(Commands::Longitude, lon_)
namespace boost { namespace spirit { namespace traits {
#define MAP_PARSER(T, expr) \
template <> struct create_parser<T> { \
using type = decltype(qi::attr_cast<T, T>(qi::copy(expr))); \
static type const& call() { static type const s_rule = qi::attr_cast<T, T>(qi::copy(expr)); return s_rule; }; \
};
#define AUTO_MAP_PARSER(T, caption) MAP_PARSER(T, qi::skip(qi::space)[qi::lit(caption) >> '=' >> qi::auto_])
AUTO_MAP_PARSER(::Commands::Longitude, "LON")
AUTO_MAP_PARSER(::Commands::Latitude, "LAT")
} } }
#include <sstream>
int main() {
std::istringstream iss("LAT=4.3\n LON=5.0");
std::string s;
while (std::getline(iss, s)) try {
using namespace Commands;
std::cout << "Parsed '" << s << "' into " << parse<Latitude, Longitude>(s) << "\n";
} catch (std::exception const& e) {
std::cout << "'" << s << "': " << e.what() << "\n";
}
}
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/x3.hpp>
namespace Commands {
namespace x3 = boost::spirit::x3;
template <typename... T>
auto parse(std::string const& s) {
using V = boost::variant<T...>;
V v;
auto it = s.begin();
if (x3::parse(it, s.end(), parser_for(v), v))
return v;
throw std::runtime_error(std::string(__FUNCTION__) + " failed");
}
struct Latitude { double lat_; };
struct Longitude { double lon_; };
auto label_for(Latitude) { return "LAT"; }
auto label_for(Longitude) { return "LON"; }
template <typename T, typename P>
auto as_cmd(P p) { return x3::rule<struct _, T>{}
= x3::skip(x3::space)[x3::lit(label_for(T{})) >> '=' >> p]; }
template <typename T> auto parser_for(T) { return as_cmd<T>(x3::double_); }
template <typename... T> auto parser_for(boost::variant<T...> _) { return (parser_for(T{}) | ...); }
static inline std::ostream& operator<<(std::ostream& os, Latitude const &t) { return os << "Latitude = " << t.lat_ << " deg"; }
static inline std::ostream& operator<<(std::ostream& os, Longitude const &t) { return os << "Longitude = " << t.lon_ << " deg"; }
}
BOOST_FUSION_ADAPT_STRUCT(Commands::Latitude, lat_)
BOOST_FUSION_ADAPT_STRUCT(Commands::Longitude, lon_)
#include <iostream>
#include <sstream>
int main() {
std::istringstream iss("LAT=4.3\n LON=5.0");
std::string s;
while (std::getline(iss, s)) try {
using namespace Commands;
std::cout << "Parsed '" << s << "' into " << parse<Latitude, Longitude>(s) << "\n";
} catch (std::exception const& e) {
std::cout << "'" << s << "': " << e.what() << "\n";
}
}
好东西
如果不使用qi::rule
也不需要硬编码迭代器。让我们进入充满乐趣的模式,也摆脱访客:
[在线直播](
除了我给出的:
如果您有能力启用c++1z,则可以将Spirit X3与折叠表达式一起使用:
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace Commands {
namespace qi = boost::spirit::qi;
template <class T> using Rule = qi::rule<std::string::const_iterator, T()>;
template <typename... T>
auto parse(std::string const& s) {
boost::variant<T...> v;
auto it = s.begin();
if (qi::parse(it, s.end(), qi::auto_, v))
return v;
throw std::runtime_error(std::string(__FUNCTION__) + " failed");
}
}
struct Latitude { double lat_; };
BOOST_FUSION_ADAPT_STRUCT(Latitude, lat_)
struct Longitude { double lon_; };
BOOST_FUSION_ADAPT_STRUCT(Longitude, lon_)
namespace boost { namespace spirit { namespace traits {
template <> struct create_parser<Latitude> {
using type = Commands::Rule<Latitude>;
static type const& call() {
static type const s_rule = qi::skip(qi::space)["LAT=" >> qi::auto_];
return s_rule;
};
};
template <> struct create_parser<Longitude> {
using type = Commands::Rule<Longitude>;
static type const& call() {
static type const s_rule = qi::skip(qi::space)["LON=" >> qi::auto_];
return s_rule;
};
};
} } }
struct print {
using result_type = void;
void operator()(Latitude const &t) const { std::cout << "Latitude = " << t.lat_ << " deg" << std::endl; }
void operator()(Longitude const &t) const { std::cout << "Longitude = " << t.lon_ << " deg" << std::endl; }
};
#include <sstream>
int main() {
std::istringstream iss("LAT=4.3\n LON=5.0");
std::string s;
print printer;
while (std::getline(iss, s)) try {
auto v = Commands::parse<Latitude, Longitude>(s);
boost::apply_visitor(printer, v);
}
catch (std::exception const& e) {
std::cout << "'" << s << "': " << e.what() << "\n";
}
}
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace Commands {
template <typename... T>
auto parse(std::string const& s) {
boost::variant<T...> v;
auto it = s.begin();
if (boost::spirit::qi::parse(it, s.end(), boost::spirit::qi::auto_, v))
return v;
throw std::runtime_error(std::string(__FUNCTION__) + " failed");
}
struct Latitude { double lat_; };
struct Longitude { double lon_; };
static inline std::ostream& operator<<(std::ostream& os, Latitude const &t) { return os << "Latitude = " << t.lat_ << " deg"; }
static inline std::ostream& operator<<(std::ostream& os, Longitude const &t) { return os << "Longitude = " << t.lon_ << " deg"; }
}
BOOST_FUSION_ADAPT_STRUCT(Commands::Latitude, lat_)
BOOST_FUSION_ADAPT_STRUCT(Commands::Longitude, lon_)
namespace boost { namespace spirit { namespace traits {
#define MAP_PARSER(T, expr) \
template <> struct create_parser<T> { \
using type = decltype(qi::attr_cast<T, T>(qi::copy(expr))); \
static type const& call() { static type const s_rule = qi::attr_cast<T, T>(qi::copy(expr)); return s_rule; }; \
};
#define AUTO_MAP_PARSER(T, caption) MAP_PARSER(T, qi::skip(qi::space)[qi::lit(caption) >> '=' >> qi::auto_])
AUTO_MAP_PARSER(::Commands::Longitude, "LON")
AUTO_MAP_PARSER(::Commands::Latitude, "LAT")
} } }
#include <sstream>
int main() {
std::istringstream iss("LAT=4.3\n LON=5.0");
std::string s;
while (std::getline(iss, s)) try {
using namespace Commands;
std::cout << "Parsed '" << s << "' into " << parse<Latitude, Longitude>(s) << "\n";
} catch (std::exception const& e) {
std::cout << "'" << s << "': " << e.what() << "\n";
}
}
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/x3.hpp>
namespace Commands {
namespace x3 = boost::spirit::x3;
template <typename... T>
auto parse(std::string const& s) {
using V = boost::variant<T...>;
V v;
auto it = s.begin();
if (x3::parse(it, s.end(), parser_for(v), v))
return v;
throw std::runtime_error(std::string(__FUNCTION__) + " failed");
}
struct Latitude { double lat_; };
struct Longitude { double lon_; };
auto label_for(Latitude) { return "LAT"; }
auto label_for(Longitude) { return "LON"; }
template <typename T, typename P>
auto as_cmd(P p) { return x3::rule<struct _, T>{}
= x3::skip(x3::space)[x3::lit(label_for(T{})) >> '=' >> p]; }
template <typename T> auto parser_for(T) { return as_cmd<T>(x3::double_); }
template <typename... T> auto parser_for(boost::variant<T...> _) { return (parser_for(T{}) | ...); }
static inline std::ostream& operator<<(std::ostream& os, Latitude const &t) { return os << "Latitude = " << t.lat_ << " deg"; }
static inline std::ostream& operator<<(std::ostream& os, Longitude const &t) { return os << "Longitude = " << t.lon_ << " deg"; }
}
BOOST_FUSION_ADAPT_STRUCT(Commands::Latitude, lat_)
BOOST_FUSION_ADAPT_STRUCT(Commands::Longitude, lon_)
#include <iostream>
#include <sstream>
int main() {
std::istringstream iss("LAT=4.3\n LON=5.0");
std::string s;
while (std::getline(iss, s)) try {
using namespace Commands;
std::cout << "Parsed '" << s << "' into " << parse<Latitude, Longitude>(s) << "\n";
} catch (std::exception const& e) {
std::cout << "'" << s << "': " << e.what() << "\n";
}
}
尝试使用
rule\uu=rule\uu.copy()|…
除了将语法存储在元组中之外。谢谢,它工作得很好。你能解释一下问题是什么吗?也许会有帮助。@llonesmiz或or。不过,在这种情况下,我只需要使用自动解析器。@llonesmiz添加了一个答案,说明了这种方法非常感谢,你的解决方案非常有趣。我真正想做的是s只使用一个模板参数调用parse
函数,因此需要一个包含各种简单语法的“容器”。嗯?好了:。即使出于某些/其他/原因,您确实需要一个qi::grammar
实例而不是qi::auto
,您也可以在~3行代码中添加它:()
Parsed 'LAT=4.3' into Latitude = 4.3 deg
Parsed ' LON=5.0' into Longitude = 5 deg
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/x3.hpp>
namespace Commands {
namespace x3 = boost::spirit::x3;
template <typename... T>
auto parse(std::string const& s) {
using V = boost::variant<T...>;
V v;
auto it = s.begin();
if (x3::parse(it, s.end(), parser_for(v), v))
return v;
throw std::runtime_error(std::string(__FUNCTION__) + " failed");
}
struct Latitude { double lat_; };
struct Longitude { double lon_; };
auto label_for(Latitude) { return "LAT"; }
auto label_for(Longitude) { return "LON"; }
template <typename T, typename P>
auto as_cmd(P p) { return x3::rule<struct _, T>{}
= x3::skip(x3::space)[x3::lit(label_for(T{})) >> '=' >> p]; }
template <typename T> auto parser_for(T) { return as_cmd<T>(x3::double_); }
template <typename... T> auto parser_for(boost::variant<T...> _) { return (parser_for(T{}) | ...); }
static inline std::ostream& operator<<(std::ostream& os, Latitude const &t) { return os << "Latitude = " << t.lat_ << " deg"; }
static inline std::ostream& operator<<(std::ostream& os, Longitude const &t) { return os << "Longitude = " << t.lon_ << " deg"; }
}
BOOST_FUSION_ADAPT_STRUCT(Commands::Latitude, lat_)
BOOST_FUSION_ADAPT_STRUCT(Commands::Longitude, lon_)
#include <iostream>
#include <sstream>
int main() {
std::istringstream iss("LAT=4.3\n LON=5.0");
std::string s;
while (std::getline(iss, s)) try {
using namespace Commands;
std::cout << "Parsed '" << s << "' into " << parse<Latitude, Longitude>(s) << "\n";
} catch (std::exception const& e) {
std::cout << "'" << s << "': " << e.what() << "\n";
}
}
Parsed 'LAT=4.3' into Latitude = 4.3 deg
Parsed ' LON=5.0' into Longitude = 5 deg