带有boost变量递归包装器的字符串解析器 [英] String parser with boost variant recursive wrapper
问题描述
下面的代码(改编自spirit qi mini_xml示例)不编译。与具有递归 boost :: variant
的属性的规则 brac
有关的错误。
然而,所有注释掉的版本的 brac
做编译。
我很想知道在这种情况下,简单的字符串解析器是如此特殊:
#include< boost / config / warning_disable.hpp>
#include< boost / spirit / include / qi.hpp>
#include< boost / spirit / include / phoenix_core.hpp>
#include< boost / spirit / include / phoenix_operator.hpp>
#include< boost / spirit / include / phoenix_fusion.hpp>
#include< boost / spirit / include / phoenix_stl.hpp>
#include< boost / spirit / include / phoenix_object.hpp>
#include< boost / fusion / include / adapt_struct.hpp>
#include< boost / variant / recursive_variant.hpp>
#include< string>
#include< vector>
命名空间客户端
{
命名空间fusion = boost :: fusion;
namespace phoenix = boost :: phoenix;
namespace qi = boost :: spirit :: qi;
namespace ascii = boost :: spirit :: ascii;
struct ast_node;
typedef boost :: variant<
boost :: recursive_wrapper< ast_node>,
std :: string
>阿斯布兰奇
struct ast_node
{
std :: string text;
std :: vector< ast_branch>儿童;
};
}
BOOST_FUSION_ADAPT_STRUCT(
client :: ast_node,
(std :: string,text)
(std :: vector< client :: ast_branch> ;,children)
)
命名空间客户端
{
template< typename Iterator>
struct ast_node_grammar
:qi :: grammar< Iterator,ast_branch(),ascii :: space_type>
{
ast_node_grammar()
:ast_node_grammar :: base_type(brac)
{
使用qi :: _ 1;
使用qi :: _ val;
using ascii :: char_;
using ascii :: string;
name%= * char_;
brac%= string(no way);
// brac = string(works)[_ val = _1];
// brac%= string(this)| string(works);
// brac%= name; // works
// brac%= * char_; // works
}
qi :: rule< Iterator,std :: string()>名称;
qi :: rule< Iterator,ast_branch(),ascii :: space_type>布拉克
};
}
int main(int argc,char ** argv)
{
typedef client :: ast_node_grammar< std :: string :: const_iterator> ; ast_node_grammar;
ast_node_grammar gram
client :: ast_branch ast;
std :: string text(dummy);
using boost :: spirit :: ascii :: space;
std :: string :: const_iterator iter = text.begin();
std :: string :: const_iterator end = text.end();
bool r = phrase_parse(iter,end,gram,space,ast);
return r? 0:1;
}
错误讯息的一部分:
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:38:17:错误:不匹配'boost :: variant<
boost :: recursive_wrapper< client :: ast_node>,basic_string< char>
> :: variant(
const __normal_iterator< const char *,basic_string< char>&& const __normal_iterator<
const char *,basic_string< char>&提前感谢。
<解决方案我建议的问题是属性兼容性。与文档相反,ascii :: string解析器显示为显示迭代器范围而不是字符串。
name = string(no way);
没有问题,因为ascii :: string公开的属性可以强制转换为规则的属性类型没有任何困难。
但是, brac
规则的属性类型是 ast_branch
,这只是其中一个可能包含的类型的变体。因此, ast_branch
类型有几个构造函数,Spirit不清楚哪一个对于这个特定的转换是足够的。
有几种方法(除了已经显示的方法):
-
使用 attr_cast
brac = qi :: attr_cast(string(no way));
-
使用 as_string
brac = qi :: as_string [string(no way)];
-
使用自订点
namespace boost {namespace spirit {namespace traits {
template< typename It>
struct assign_to_attribute_from_iterators< client :: ast_branch,It>
{
static void call(It const& f,It const& l,client :: ast_branch& val)
{
val = std :: string(f, ;
}
};
}}}
这些都有相同的效果:让Spirit意识到要使用的属性转换。
这是一个完整的工作示例,显示所有三个:
// #define BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
#include< boost / config / warning_disable.hpp>
#include< boost / spirit / include / qi.hpp>
#include< boost / fusion / adapted.hpp>
#include< boost / spirit / include / phoenix.hpp>
#include< boost / variant / recursive_variant.hpp>
#include< string>
#include< vector>
命名空间客户端
{
命名空间fusion = boost :: fusion;
namespace phoenix = boost :: phoenix;
namespace qi = boost :: spirit :: qi;
namespace ascii = boost :: spirit :: ascii;
struct ast_node;
typedef boost :: variable<
boost :: recursive_wrapper< ast_node>,
std :: string
>阿斯布兰奇
struct ast_node
{
std :: string text;
std :: vector< ast_branch>儿童;
};
}
namespace boost {namespace spirit {namespace traits {
template< typename It>
struct assign_to_attribute_from_iterators< client :: ast_branch,It>
{
static void call(It const& f,It const& l,client :: ast_branch& val)
{
val = std :: string(f, ;
}
};
}}}
BOOST_FUSION_ADAPT_STRUCT(
client :: ast_node,
(std :: string,text)
(std :: vector& :ast_branch> children)
)
命名空间客户端
{
template< typename Iterator>
struct ast_node_grammar:qi :: grammar< Iterator,ast_branch(),ascii :: space_type>
{
ast_node_grammar()
:ast_node_grammar :: base_type(brac)
{
使用qi :: _ 1;
使用qi :: _ val;
using ascii :: char_;
using ascii :: string;
name%= * char_;
brac = string(works);
brac = string(works)[_ val = _1];
brac%= string(this)| string(works);
brac%= name; // works
brac%= * char_; // works
brac = qi :: as_string [string(no way)];
brac = qi :: attr_cast(string(no way));
}
qi :: rule< Iterator,std :: string()>名称;
qi :: rule< Iterator,ast_branch(),ascii :: space_type>布拉克
};
}
int main(int argc,char ** argv)
{
typedef client :: ast_node_grammar< std :: string :: const_iterator> ; ast_node_grammar;
ast_node_grammar gram
client :: ast_branch ast;
std :: string text(dummy);
using boost :: spirit :: ascii :: space;
std :: string :: const_iterator iter = text.begin();
std :: string :: const_iterator end = text.end();
bool r = phrase_parse(iter,end,gram,space,ast);
return r? 0:1;
}
The code below (adapted from spirit qi mini_xml example) does not compile. There is an error related to the rule brac
that has an attribute of an recursive boost::variant
.
However, all commented out versions of brac
do compile.
I am very curious to know what makes the simple string parser so special in this case:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <string>
#include <vector>
namespace client
{
namespace fusion = boost::fusion;
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
struct ast_node;
typedef boost::variant<
boost::recursive_wrapper<ast_node>,
std::string
> ast_branch;
struct ast_node
{
std::string text;
std::vector<ast_branch> children;
};
}
BOOST_FUSION_ADAPT_STRUCT(
client::ast_node,
(std::string, text)
(std::vector<client::ast_branch>, children)
)
namespace client
{
template <typename Iterator>
struct ast_node_grammar
: qi::grammar<Iterator, ast_branch(), ascii::space_type>
{
ast_node_grammar()
: ast_node_grammar::base_type(brac)
{
using qi::_1;
using qi::_val;
using ascii::char_;
using ascii::string;
name %= *char_;
brac %= string("no way") ;
// brac = string("works")[_val = _1] ;
// brac %= string("this") | string("works");
// brac %= name ; // works
// brac %= *char_ ; // works
}
qi::rule<Iterator, std::string()> name;
qi::rule<Iterator, ast_branch(), ascii::space_type> brac;
};
}
int main(int argc, char **argv)
{
typedef client::ast_node_grammar<std::string::const_iterator> ast_node_grammar;
ast_node_grammar gram;
client::ast_branch ast;
std::string text("dummy");
using boost::spirit::ascii::space;
std::string::const_iterator iter = text.begin();
std::string::const_iterator end = text.end();
bool r = phrase_parse(iter, end, gram, space, ast);
return r ? 0 : 1;
}
Part of the error message:
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:38:17: error: No match for ‘boost::variant<
boost::recursive_wrapper<client::ast_node>, basic_string<char>
>::variant(
const __normal_iterator<const char *, basic_string<char> > &, const __normal_iterator<
const char *, basic_string<char> > &)’
Thank's in advance.
解决方案 I'd suggest the problem is Attribute compatibility. Contrary to the documentation, the ascii::string parser appears to expose an iterator range instead of a string.
name = string("no way");
is no issue, because the attribute exposed by ascii::string can be coerced into the rule's attribute type without any difficulty.
However, the brac
rule's attribute type is ast_branch
, which is only a variant with one of it's possible contained types. Therefore, the ast_branch
type has several constructors, and it isn't clear to Spirit which one is adequate for this particular conversion.
There are several ways about this (besides the approaches you already showed):
use attr_cast
brac = qi::attr_cast( string("no way") );
use as_string
brac = qi::as_string[ string("no way") ];
use customization points
namespace boost { namespace spirit { namespace traits {
template <typename It>
struct assign_to_attribute_from_iterators<client::ast_branch, It>
{
static void call(It const& f, It const& l, client::ast_branch& val)
{
val = std::string(f, l);
}
};
}}}
Each of these have the same effect: make Spirit realize what attribute conversion to use.
Here is a full working sample showing all three:
// #define BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <string>
#include <vector>
namespace client
{
namespace fusion = boost::fusion;
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
struct ast_node;
typedef boost::variant<
boost::recursive_wrapper<ast_node>,
std::string
> ast_branch;
struct ast_node
{
std::string text;
std::vector<ast_branch> children;
};
}
namespace boost { namespace spirit { namespace traits {
template <typename It>
struct assign_to_attribute_from_iterators<client::ast_branch, It>
{
static void call(It const& f, It const& l, client::ast_branch& val)
{
val = std::string(f, l);
}
};
}}}
BOOST_FUSION_ADAPT_STRUCT(
client::ast_node,
(std::string, text)
(std::vector<client::ast_branch>, children)
)
namespace client
{
template <typename Iterator>
struct ast_node_grammar : qi::grammar<Iterator, ast_branch(), ascii::space_type>
{
ast_node_grammar()
: ast_node_grammar::base_type(brac)
{
using qi::_1;
using qi::_val;
using ascii::char_;
using ascii::string;
name %= *char_;
brac = string("works");
brac = string("works")[_val = _1] ;
brac %= string("this") | string("works");
brac %= name ; // works
brac %= *char_ ; // works
brac = qi::as_string[ string("no way") ];
brac = qi::attr_cast( string("no way") );
}
qi::rule<Iterator, std::string()> name;
qi::rule<Iterator, ast_branch(), ascii::space_type> brac;
};
}
int main(int argc, char **argv)
{
typedef client::ast_node_grammar<std::string::const_iterator> ast_node_grammar;
ast_node_grammar gram;
client::ast_branch ast;
std::string text("dummy");
using boost::spirit::ascii::space;
std::string::const_iterator iter = text.begin();
std::string::const_iterator end = text.end();
bool r = phrase_parse(iter, end, gram, space, ast);
return r ? 0 : 1;
}
这篇关于带有boost变量递归包装器的字符串解析器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!