【发布时间】:2017-08-03 13:37:25
【问题描述】:
我在使用可变参数模板定义语法时遇到了问题。
我首先定义了一些包含在某些结构(例如纬度、经度)中的简单语法,如下所示:
#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_))
然后,我想将它们组合成一个完整的语法,能够解析属于任何这些简单语法的字符串。为此,我定义了一个可变参数模板结构,它试图将子语法列表扩展为“语法1 | 语法2 | ...”这样的表达式
template <class... Commands>
struct device_grammar : boost::spirit::qi::grammar<
std::string::iterator,
boost::variant<typename Commands::return_type...>(),
boost::spirit::ascii::space_type> {
typedef boost::variant<typename Commands::return_type...> return_type;
device_grammar() : device_grammar::base_type{rule_}{
build_rule<typename Commands::grammar...>();
}
private:
template <class CommandGrammar> void build_rule() {
rule_ = CommandGrammar();
}
template <class FirstGrammar, class SecondGrammar, class... Others>
void build_rule() {
build_rule<SecondGrammar, Others...>();
rule_ = rule_ | FirstGrammar();
}
boost::spirit::qi::rule<std::string::iterator, return_type(),
boost::spirit::ascii::space_type>
rule_;
};
typedef device_grammar<Latitude, Longitude> CoordinatesGrammar;
代码编译(参见下面的完整示例);问题是当它尝试解析输入字符串时,会产生分段错误。 有人可以帮我解决这个问题吗?
非常感谢。
示例代码(g++-4.9 或 clang++-3.9):
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <iostream>
#include <string>
template <class... Commands>
struct device_grammar : boost::spirit::qi::grammar<
std::string::iterator,
boost::variant<typename Commands::return_type...>(),
boost::spirit::ascii::space_type> {
typedef boost::variant<typename Commands::return_type...> return_type;
device_grammar() : device_grammar::base_type{rule_}{
build_rule<typename Commands::grammar...>();
}
private:
template <class CommandGrammar> void build_rule() {
rule_ = CommandGrammar();
}
template <class FirstGrammar, class SecondGrammar, class... Others>
void build_rule() {
build_rule<SecondGrammar, Others...>();
rule_ = rule_ | FirstGrammar();
}
boost::spirit::qi::rule<std::string::iterator, return_type(),
boost::spirit::ascii::space_type>
rule_;
};
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_))
typedef device_grammar<Latitude, Longitude> CoordinatesGrammar;
struct print : public boost::static_visitor<> {
void operator()(Latitude::return_type &t) const {
std::cout << "Latitude = " << t.lat_ << " deg" << std::endl;
;
}
void operator()(Longitude::return_type &t) const {
std::cout << "Longitude = " << t.lon_ << " deg" << std::endl;
;
}
};
int main() {
std::string s;
CoordinatesGrammar g;
CoordinatesGrammar::return_type v;
while (1) {
std::getline(std::cin, s);
auto it = s.begin();
if (qi::phrase_parse(it, s.end(), g, ascii::space, v)) {
print p;
boost::apply_visitor(p, v);
}
}
return 0;
}
编辑: 据我了解,问题出在线条上
rule_ = CommandGrammar();
...
rule_ = rule_ | FirstGrammar();
似乎语法对象不能是临时的,必须作为类的成员存储。我该怎么做?
编辑:
我也尝试将此类对象存储在 std::tuple 中,但它似乎仍然无法正常工作。
【问题讨论】:
-
尝试使用
rule_ = rule_.copy() |...除了将语法存储在元组中。 -
谢谢,它运行良好。你能解释一下是什么问题吗?
-
@llonesmiz 或 stackoverflow.com/questions/17405500/… 或 stackoverflow.com/questions/27812715/…。不过,在这种情况下,我会简单地使用自动解析器。
-
@llonesmiz 添加了一个显示该方法的答案
标签: c++ parsing variadic-templates boost-spirit-qi