以增强精神来定义用子份子参数的解析器
Define parsers parameterized with sub-parsers in Boost Spirit
我想将一些旧的手写解析代码转换为在此过程中提高精神和学习(更多)精神。旧代码使用流和模板来解析某些数据类型和某些容器的定义。
一些典型格式:
VECTOR[number_of_items,(item_1, item_2 .... item_n)]
PAIR(p1, p2)
RECT[(left,top)-(right,bottom)]
Point( x, y )
Size( x, y )
解析函数是模板,其中项目类型为模板参数,并将流作为输入,例如
template<class T> std::istream& operator>>(std::Stream& in, std::vector<T>& v);
template<class T1, class T2> std::istream& operator>>(std::istream& in, std::pair<T1, T2>& p);
template<class T1, class T2> std::istream& operator>>(std::istream& in, RectType<T>& r);
etc.
矢量的解析器(流提取器)调用模板类型的解析器。
使用这些可以解析整数矩形,双矩形和成对的字符串和整数向量的定义。
有可能用精神编写模板类型的sub-parser的模板解析器?
作为另一个答案几乎已经明确了,Qi已经有一种机制可以生成属性类型。
最终用户面对位的位是qi::auto_
。qi::auto_
是 Parser ,而不是 grammar 。
这具有不同的优势 [1] 。
- 最重要的是,它允许用户在自己选择的 skipper 的语法内使用解析器,并可能使用
qi::locals<>
。 - 另外,
auto_
Qi表达式终端已经定义,因此根本没有必要使用详细模板参数列表来实例化语法: - 最后,解析器返回一个表达模板,因此没有类型的呼吸器进行,并且以这种方式组合几个自动_解析器的效率并不比手动组成语法(而均包含
qi::rule<>
和qi::grammar<>
Incur conc_6 Incur性能开销)
让我们看看它的使用方式:
std::vector<std::pair<double, int> > parsed;
bool result_ = qi::phrase_parse(first, last, qi::auto_, qi::space, parsed);
您可以看到,这可容纳船长,并且"神奇地"选择与parsed
匹配的解析器。现在,要从OP获取样品格式,您需要挂钩进入auto_
Parser的自定义点:
namespace boost { namespace spirit { namespace traits {
// be careful copying expression templates. Boost trunk has `qi::copy` for this too, now
#define PARSER_DEF(a) using type = decltype(boost::proto::deep_copy(a)); static type call() { return boost::proto::deep_copy(a); }
template<typename T1, typename T2>
struct create_parser<std::pair<T1, T2> >
{
PARSER_DEF('(' >> create_parser<T1>::call() >> ',' >> create_parser<T2>::call() >> ')');
};
template<typename TV, typename... TArgs>
struct create_parser<std::vector<TV, TArgs...> >
{
PARSER_DEF('[' >> qi::omit[qi::uint_] >> ',' >> '(' >> create_parser<TV>::call() % ',' >> ')' >> ']' );
};
#undef PARSER_DEF
} } }
这实际上就是所需要的。这是一个解析的演示:
VECTOR[ 1 ,
(
PAIR (0.97,
5),
PAIR (1.75,10)
)
]
并将解析的数据打印为:
Parsed:
0.97 5
1.75 10
请参阅 Live on Coliru
完整代码列表
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/qi.hpp>
namespace qi = boost::spirit::qi;
namespace boost { namespace spirit { namespace traits {
// be careful copying expression templates. Boost trunk has `qi::copy` for this too, now
#define PARSER_DEF(a) using type = decltype(boost::proto::deep_copy(a)); static type call() { return boost::proto::deep_copy(a); }
template<typename T1, typename T2>
struct create_parser<std::pair<T1, T2> >
{
PARSER_DEF(lexeme [ lit("PAIR") ] >> '(' >> create_parser<T1>::call() >> ',' >> create_parser<T2>::call() >> ')');
};
template<typename TV, typename... TArgs>
struct create_parser<std::vector<TV, TArgs...> >
{
PARSER_DEF(lexeme [ lit("VECTOR") ] >> '[' >> qi::omit[qi::uint_] >> ',' >> '(' >> create_parser<TV>::call() % ',' >> ')' >> ']' );
};
#undef PARSER_DEF
} } }
#include <boost/spirit/home/karma.hpp>
namespace karma = boost::spirit::karma;
int main()
{
std::string const input("VECTOR[ 1 ,n"
" ( n"
" PAIR (0.97, n"
" 5), n"
" PAIR (1.75,10) n"
" ) n"
"]");
std::cout << input << "nn";
auto first = input.begin();
auto last = input.end();
std::vector<std::pair<double, int> > parsed;
bool result_ = qi::phrase_parse(first, last, qi::auto_, qi::space, parsed);
if (first!=last)
std::cout << "Remaining unparsed input: '" << std::string(first, last) << "'n";
if (result_)
std::cout << "Parsed:n " << karma::format_delimited(karma::auto_ % karma::eol, " ", parsed) << "n";
else
std::cout << "Parsing did not succeedn";
}
[1] 潜在的缺点是定制点是固定的,因此您只能关联1 auto_
带有任何类型的解析器。滚动自己的基本模板可为您提供更多的控制权,并使您(更多)可以轻松具有不同的"解析器风味"。但是,最终有可能两全其美,所以我首先要方便。
是的,这是可能的。我会以这种方式实施
#include <boost/spirit/home/qi.hpp>
namespace qi = boost::spirit::qi;
template < typename _Type, typename _Iterator, typename _Enable = void >
struct parser;
template < typename _Type, typename _Iterator >
struct parser < _Type, _Iterator, typename std::enable_if < std::is_arithmetic<_Type> ::value > ::type > :
qi::grammar<_Iterator, _Type() >
{
parser()
: parser::base_type(impl)
{
impl = qi::create_parser<_Type>() ;
}
qi::rule<_Iterator, _Type()> impl;
};
template < typename _Iterator >
struct parser < double, _Iterator> :
qi::grammar<_Iterator, double() >
{
parser()
: parser::base_type(impl)
{
impl = qi::double_;
}
qi::rule<_Iterator, double()> impl;
};
template < typename _First, typename _Second, typename _Iterator >
struct parser < std::pair<_First, _Second>, _Iterator> :
qi::grammar<_Iterator, std::pair<_First, _Second>() >
{
parser()
: parser::base_type(impl)
{
impl = qi::lit('(') >> first >> ',' >> second >> ')';
}
qi::rule<_Iterator, std::pair<_First, _Second>()> impl;
parser<_First, _Iterator> first;
parser<_Second, _Iterator> second;
};
template < typename _Type, typename _Alloc, typename _Iterator >
struct parser < std::vector<_Type, _Alloc>, _Iterator> :
qi::grammar<_Iterator, std::vector<_Type, _Alloc>() >
{
parser()
: parser::base_type(impl)
{
impl = qi::lit('[') >> qi::omit[qi::uint_] >> ",(" >> item % ',' >> ")]";
}
qi::rule<_Iterator, std::vector<_Type, _Alloc>()> impl;
parser<_Type, _Iterator> item;
};
用法示例:
int main(int agrc, char *argv[])
{
typedef std::pair<double, int> pair;
using string = std::string;
string input_ = { "[1,((0.97,5),(1.75,10))]" };
string::const_iterator iterator_ = input_.begin();
string::const_iterator end_ = input_.end();
std::vector<pair> pairs_;
bool result_ = qi::parse(iterator_, end_, parser <std::vector<pair>, string::const_iterator> (), pairs_);
return 0;
}
相关文章:
- 如何反转整数参数包
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 如何使用默认参数等选择模板专业化
- 模板参数替换失败,并且未完成隐式转换
- 具有默认模板参数的多态类的模板推导失败
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 函数调用中参数的顺序重要吗
- 部分定义/别名模板模板参数
- 模板-模板参数推导:三个不同的编译器三种不同的行为
- 使用不带参数的函数访问结构元素
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 传递参数以增强集成符
- 增强Python参数错误
- 使用引用参数增强 Python 方法调用
- 以增强精神来定义用子份子参数的解析器
- 如何在用于增强现实的OpenCV上使用相机校准参数
- 使用默认参数增强list_of
- C++具有不同参数的增强信号和插槽
- 增强UTF的命令行参数unit_test_main