如何使用 qi 创建通用解析器?
How do you create a generic parser using qi?
我正在尝试使用 qi 创建通用解析器元素,因为不幸的是(必须支持 MSVC)无法使用 X3。
这个想法是有一个模板化的结构:
template<class T> struct parse_type;
我可以这样使用:
template<class T> T from_string(std::string const& s)
{
T res;
parse_type<T> t;
...
if (phrase_parse(...,parse_type<T>(),...,t))
}
或像这样专攻
template<class T,class Alloc>
struct parse_type<std::vector<T,Alloc>>
{
// Parse a vector using rule '[' >> parse_type<T> % ',' > ']';
}
主要目的是允许轻松解析例如std::tuple、boost::optional和boost::variant(最后一个由于气的贪婪性,不能自动)。
对于如何处理此问题的反馈,我将不胜感激。目前我的结构基于 qi::grammar,但 X3 不支持语法,我想在 MSVC 编译它时使用 X3,而且我对必须提供船长也有点不舒服。
另一种方法是在 parse_type 中有一个静态函数 returns 适当的规则。我正在考虑这是否是一种更清洁的方法?
如有任何反馈,我们将不胜感激。
更新 2:用在运行时失败的可编译示例替换了代码片段。这是代码:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <string>
#include <iostream>
#include <iostream>
// Support to simplify
using iter = std::string::const_iterator;
void print(std::vector<int> const& v)
{
std::cout << '[';
for (auto i: v) std::cout << i << ',';
std::cout << "]";
}
namespace qi = boost::spirit::qi;
// My rule factory - quite useless if you do not specialise
template<class T> struct ps_rule;
// An example of using the factory
template<class T>
T from_string(std::string const& s)
{
T result;
iter first { std::begin(s) };
auto rule = ps_rule<T>::get();
phrase_parse(first,std::end(s),rule,qi::space,result);
return result;
}
// Specialising rule for int
template<>
struct ps_rule<int>
{
static qi::rule<iter,int()> get() { return qi::int_; }
};
// ... and for std::vector (where the elements must have rules)
template<class T,class Alloc>
struct ps_rule<std::vector<T,Alloc>>
{
static qi::rule<iter,std::vector<T,Alloc>()> get()
{
qi::rule<iter,std::vector<T,Alloc>()> res;
res.name("Vector");
res =
qi::lit('{')
>> ps_rule<T>::get() % ','
>> '}';
return res;
}
};
int main()
{
// This one works like a charm.
std::cout << ((from_string<int>("100") == 100) ? "OK\n":"Failed\n");
std::vector<int> v {1,2,3,4,5,6};
// This one fails
std::cout << ((from_string<std::vector<int>>("{1,2,3,4,5,6}") == v) ? "OK\n":"Failed\n");
}
代码在 boost/function_template.hpp 第 766 行失败:
result_type operator()(BOOST_FUNCTION_PARMS) const
{
if (this->empty())
boost::throw_exception(bad_function_call());
return get_vtable()->invoker
(this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS);
}
这段代码是boost::function4中的一个成员函数
,boost::fusion::vector0 > &
,boost::spirit::unused_type const&>
问题是 get_vtable returns 一个无效指针。
你的主要问题是 qi::rule
的复制构造函数引用了原始规则,在你的情况下它是一个局部变量。避免此问题的一种方法是使用 qi::rule
的 copy
成员函数,但这需要稍微更改 ps_rule
.[=20= 专业化的 return 类型]
static typename boost::proto::terminal<qi::rule<iter,std::vector<T,Alloc>()>>::type get()
{
//[...] (same as before)
return res.copy();
}
一旦你这样做了,你的 ps_rule<int>
就会出现同样的问题,尽管它似乎是独立工作的。您可以做一些类似的事情,但在这种情况下不需要规则,最好(即使从性能的角度来看)只使用类似的东西:
static qi::int_type get() { return qi::int_; }
Full Sample (Running on WandBox)
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
// Support to simplify
using iter = std::string::const_iterator;
void print(std::vector<int> const& v)
{
std::cout << '[';
for (auto i: v) std::cout << i << ',';
std::cout << "]";
}
namespace qi = boost::spirit::qi;
// My rule factory - quite useless if you do not specialise
template<class T> struct ps_rule;
// An example of using the factory
template<class T>
T from_string(std::string const& s)
{
T result;
iter first { std::begin(s) };
auto rule = ps_rule<T>::get();
qi::phrase_parse(first,std::end(s),rule,qi::space,result);
return result;
}
// Specialising rule for int
template<>
struct ps_rule<int>
{
static qi::int_type get() { return qi::int_; }
};
// ... and for std::vector (where the elements must have rules)
template<class T,class Alloc>
struct ps_rule<std::vector<T,Alloc>>
{
static typename boost::proto::terminal<qi::rule<iter,std::vector<T,Alloc>()>>::type get()
{
qi::rule<iter,std::vector<T,Alloc>()> res;
res.name("Vector");
res =
qi::lit('{')
>> ps_rule<T>::get() % ','
>> '}';
return res.copy();
}
};
int main()
{
// This one works like a charm.
std::cout << ((from_string<int>("100") == 100) ? "OK\n":"Failed\n");
std::vector<int> v {1,2,3,4,5,6};
std::cout << ((from_string<std::vector<int>>("{1,2,3,4,5,6}") == v) ? "OK\n":"Failed\n");
std::vector<std::vector<int> > vv {{1,2,3},{4,5,6}};
std::cout << ((from_string<std::vector<std::vector<int>>>("{{1,2,3},{4,5,6}}") == vv) ? "OK\n":"Failed\n");
}
PS:如果你使用Spirit's own machinery to create parsers automatically in your primary template. Here is an example.
,你可以节省很多专业化
我正在尝试使用 qi 创建通用解析器元素,因为不幸的是(必须支持 MSVC)无法使用 X3。 这个想法是有一个模板化的结构:
template<class T> struct parse_type;
我可以这样使用:
template<class T> T from_string(std::string const& s)
{
T res;
parse_type<T> t;
...
if (phrase_parse(...,parse_type<T>(),...,t))
}
或像这样专攻
template<class T,class Alloc>
struct parse_type<std::vector<T,Alloc>>
{
// Parse a vector using rule '[' >> parse_type<T> % ',' > ']';
}
主要目的是允许轻松解析例如std::tuple、boost::optional和boost::variant(最后一个由于气的贪婪性,不能自动)。
对于如何处理此问题的反馈,我将不胜感激。目前我的结构基于 qi::grammar,但 X3 不支持语法,我想在 MSVC 编译它时使用 X3,而且我对必须提供船长也有点不舒服。 另一种方法是在 parse_type 中有一个静态函数 returns 适当的规则。我正在考虑这是否是一种更清洁的方法?
如有任何反馈,我们将不胜感激。
更新 2:用在运行时失败的可编译示例替换了代码片段。这是代码:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <string>
#include <iostream>
#include <iostream>
// Support to simplify
using iter = std::string::const_iterator;
void print(std::vector<int> const& v)
{
std::cout << '[';
for (auto i: v) std::cout << i << ',';
std::cout << "]";
}
namespace qi = boost::spirit::qi;
// My rule factory - quite useless if you do not specialise
template<class T> struct ps_rule;
// An example of using the factory
template<class T>
T from_string(std::string const& s)
{
T result;
iter first { std::begin(s) };
auto rule = ps_rule<T>::get();
phrase_parse(first,std::end(s),rule,qi::space,result);
return result;
}
// Specialising rule for int
template<>
struct ps_rule<int>
{
static qi::rule<iter,int()> get() { return qi::int_; }
};
// ... and for std::vector (where the elements must have rules)
template<class T,class Alloc>
struct ps_rule<std::vector<T,Alloc>>
{
static qi::rule<iter,std::vector<T,Alloc>()> get()
{
qi::rule<iter,std::vector<T,Alloc>()> res;
res.name("Vector");
res =
qi::lit('{')
>> ps_rule<T>::get() % ','
>> '}';
return res;
}
};
int main()
{
// This one works like a charm.
std::cout << ((from_string<int>("100") == 100) ? "OK\n":"Failed\n");
std::vector<int> v {1,2,3,4,5,6};
// This one fails
std::cout << ((from_string<std::vector<int>>("{1,2,3,4,5,6}") == v) ? "OK\n":"Failed\n");
}
代码在 boost/function_template.hpp 第 766 行失败:
result_type operator()(BOOST_FUNCTION_PARMS) const
{
if (this->empty())
boost::throw_exception(bad_function_call());
return get_vtable()->invoker
(this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS);
}
这段代码是boost::function4中的一个成员函数 ,boost::fusion::vector0 > & ,boost::spirit::unused_type const&> 问题是 get_vtable returns 一个无效指针。
你的主要问题是 qi::rule
的复制构造函数引用了原始规则,在你的情况下它是一个局部变量。避免此问题的一种方法是使用 qi::rule
的 copy
成员函数,但这需要稍微更改 ps_rule
.[=20= 专业化的 return 类型]
static typename boost::proto::terminal<qi::rule<iter,std::vector<T,Alloc>()>>::type get()
{
//[...] (same as before)
return res.copy();
}
一旦你这样做了,你的 ps_rule<int>
就会出现同样的问题,尽管它似乎是独立工作的。您可以做一些类似的事情,但在这种情况下不需要规则,最好(即使从性能的角度来看)只使用类似的东西:
static qi::int_type get() { return qi::int_; }
Full Sample (Running on WandBox)
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
// Support to simplify
using iter = std::string::const_iterator;
void print(std::vector<int> const& v)
{
std::cout << '[';
for (auto i: v) std::cout << i << ',';
std::cout << "]";
}
namespace qi = boost::spirit::qi;
// My rule factory - quite useless if you do not specialise
template<class T> struct ps_rule;
// An example of using the factory
template<class T>
T from_string(std::string const& s)
{
T result;
iter first { std::begin(s) };
auto rule = ps_rule<T>::get();
qi::phrase_parse(first,std::end(s),rule,qi::space,result);
return result;
}
// Specialising rule for int
template<>
struct ps_rule<int>
{
static qi::int_type get() { return qi::int_; }
};
// ... and for std::vector (where the elements must have rules)
template<class T,class Alloc>
struct ps_rule<std::vector<T,Alloc>>
{
static typename boost::proto::terminal<qi::rule<iter,std::vector<T,Alloc>()>>::type get()
{
qi::rule<iter,std::vector<T,Alloc>()> res;
res.name("Vector");
res =
qi::lit('{')
>> ps_rule<T>::get() % ','
>> '}';
return res.copy();
}
};
int main()
{
// This one works like a charm.
std::cout << ((from_string<int>("100") == 100) ? "OK\n":"Failed\n");
std::vector<int> v {1,2,3,4,5,6};
std::cout << ((from_string<std::vector<int>>("{1,2,3,4,5,6}") == v) ? "OK\n":"Failed\n");
std::vector<std::vector<int> > vv {{1,2,3},{4,5,6}};
std::cout << ((from_string<std::vector<std::vector<int>>>("{{1,2,3},{4,5,6}}") == vv) ? "OK\n":"Failed\n");
}
PS:如果你使用Spirit's own machinery to create parsers automatically in your primary template. Here is an example.
,你可以节省很多专业化