提升::精神::气。如何将内联解析器表达式转换为独立语法,以及如何解压它们生成的元组?
我正在使用 QI 和 Phoenix,我想编写一个返回 4 个布尔值的小语法,这些布尔值将用作语义操作内函数调用的参数。
我有几个需要这些东西的函数,到目前为止我已经使用了这种方法:
( qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool)
[px::bind(&Bool4Function, spirit::_val, spirit::_1, spirit::_2, spirit::_3, spirit::_4)]
虽然它本身没问题,但在整个地方使用它只是简单的丑陋和混乱,即使“使用”命名空间部分也是如此。
这就是为什么我想将这个表达式提取到一个独立的语法中。
所以我尝试了这个(归功于 ildjarn 的测试平台):
///// grammar implementation /////
#include <boost/fusion/include/vector10.hpp>
#include <boost/spirit/include/qi_bool.hpp>
#include <boost/spirit/include/qi_char_.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi_operator.hpp>
#include <boost/spirit/include/qi_rule.hpp>
#include <boost/spirit/include/qi_string.hpp>
struct FourBools : boost::spirit::qi::grammar<
char const*,
boost::fusion::vector4<bool, bool, bool, bool>()
>
{
typedef boost::fusion::vector4<bool, bool, bool, bool> attribute_type;
FourBools() : base_type(start_)
{
using boost::spirit::bool_;
start_
= "4bools:"
>> bool_ >> ','
>> bool_ >> ','
>> bool_ >> ','
>> bool_ >> ';'
;
}
private:
boost::spirit::qi::rule<
base_type::iterator_type,
base_type::sig_type
> start_;
};
FourBools const fourBools;
///// demonstration of use /////
#include <string>
#include <ios>
#include <iostream>
#include <boost/fusion/include/at_c.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/qi_action.hpp>
#include <boost/spirit/include/qi_parse.hpp>
void noDice(bool a, bool b, bool c, bool d)
{
}
void worksFine(boost::fusion::vector4<bool, bool, bool, bool> a)
{
}
int main()
{
namespace phx = boost::phoenix;
namespace spirit = boost::spirit;
std::string const input("4bools:true,true,true,false;");
char const* first = input.c_str();
char const* const last = first + input.size();
bool const success = spirit::qi::parse(
first, last,
fourBools[phx::bind(&noDice, spirit::_1)]
);
if (!success)
std::cout << "parse() failed\n";
else if (first != last)
std::cout << "didn't consume all input\n";
std::cout.flush();
}
除非将 fourBools[phx::bind(&noDice,spirit::_1)]
替换为 ,否则无法编译fourBools[phx::bind(&worksFine,spirit::_1)]
。
这意味着,我的问题是解包参数以匹配要调用的函数的签名,因为参数的数量在签名级别不同(四个布尔值的元组,而不是四个布尔值)。
是否可以直接使用 phoenix 占位符进行解包,而不是编写将元组转换为需要将它们分开的现有函数的单独参数的包装器? 如果是的话,其语法是什么? 毕竟,像 ( qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool)
这样的内联版本在“解压”时可以正常工作通过 spirit::_1 -spirit::_4,
占位符。
在我看来,这个版本似乎也返回一个元组,并且可以通过上述方法以某种方式解包,这与返回元组的语法不同。
我该如何处理这个问题?
I'm using QI and Phoenix, and I want to write a small grammar that returns 4 bools which are to be used as arguments for a function call inside a semantic action.
I have several functions that need those things, and so far I have used this approach:
( qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool)
[px::bind(&Bool4Function, spirit::_val, spirit::_1, spirit::_2, spirit::_3, spirit::_4)]
and while it's okay on it's own, using it all over the place is just plain ugly and confusing, even with 'using' the namespace parts.
That's why I wanted to extract this expression into a standalone grammar.
So I tried this (credit goes to ildjarn for the testbed):
///// grammar implementation /////
#include <boost/fusion/include/vector10.hpp>
#include <boost/spirit/include/qi_bool.hpp>
#include <boost/spirit/include/qi_char_.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi_operator.hpp>
#include <boost/spirit/include/qi_rule.hpp>
#include <boost/spirit/include/qi_string.hpp>
struct FourBools : boost::spirit::qi::grammar<
char const*,
boost::fusion::vector4<bool, bool, bool, bool>()
>
{
typedef boost::fusion::vector4<bool, bool, bool, bool> attribute_type;
FourBools() : base_type(start_)
{
using boost::spirit::bool_;
start_
= "4bools:"
>> bool_ >> ','
>> bool_ >> ','
>> bool_ >> ','
>> bool_ >> ';'
;
}
private:
boost::spirit::qi::rule<
base_type::iterator_type,
base_type::sig_type
> start_;
};
FourBools const fourBools;
///// demonstration of use /////
#include <string>
#include <ios>
#include <iostream>
#include <boost/fusion/include/at_c.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/qi_action.hpp>
#include <boost/spirit/include/qi_parse.hpp>
void noDice(bool a, bool b, bool c, bool d)
{
}
void worksFine(boost::fusion::vector4<bool, bool, bool, bool> a)
{
}
int main()
{
namespace phx = boost::phoenix;
namespace spirit = boost::spirit;
std::string const input("4bools:true,true,true,false;");
char const* first = input.c_str();
char const* const last = first + input.size();
bool const success = spirit::qi::parse(
first, last,
fourBools[phx::bind(&noDice, spirit::_1)]
);
if (!success)
std::cout << "parse() failed\n";
else if (first != last)
std::cout << "didn't consume all input\n";
std::cout.flush();
}
That doesn't compile unless fourBools[phx::bind(&noDice, spirit::_1)]
is replaced with fourBools[phx::bind(&worksFine, spirit::_1)]
.
That means, my problem is the unpacking of arguments to match the signature of the function to be called, since the number of arguments differ at signature level (one tuple of four bools, vs four bools on their own).
Is it possible to unpack using phoenix placeholders directly, instead of writing wrappers which translate tuples into individual arguments for my existing functions that need them separate?
If it is, what would be the syntax for that?
After all, an inline version like ( qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool)
works fine when 'unpacked' by spirit::_1 - spirit::_4,
placeholders.
That makes it appear to me as if this version returns a tuple as well, and is somehow unpackable with the above approach, unlike a grammar that returns one.
How do I deal with this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果您不发布完整、连贯的重现,则几乎不可能诊断您的问题;这可能是一个语法错误,也可能是缺少
#include
,谁知道......?这是一个工作演示;希望您可以使用它作为参考来找出您的代码有什么问题:
顺便说一句,我认为使用具有纯同质类型的元组很奇怪;就我个人而言,我会将语法的综合属性更改为
boost::array
。编辑(回应OP的编辑):有好消息、坏消息和更多好消息。
好消息是:Boost.Fusion 的功能可以完全满足您的需求最小代码: <代码>boost::fusion::fused<>。这将采用带有多个参数的可调用类型(包括自由函数指针和成员函数指针),并将该可调用类型包装在采用 Fusion 序列的仿函数中;当调用此函子时,它会获取 Fusion 序列并将其解包,将元组的各个元素作为单独的参数转发到包装的可调用类型。
因此,考虑到我已经发布的语法和以下内容:
boost::spirit::qi::parse()
可以像这样调用:这是坏消息:Boost.Fusion 的可调用类型包装器依赖于TR1/C++11
result_of
协议,而 Boost.Phoenix v2 实现了 Boost.Lambdaresult_of
协议 - 这些是不兼容的。因此,您必须自己解压元组元素:糟糕!不过,还有更多好消息:Boost.Phoenix v3 即将在 Boost 1.47 中发布,它实现了 TR1/C++11
result_of
协议。因此,从 Boost 1.47 开始,您将能够使用boost::fusion::fused
并节省一些繁琐的样板文件。It's pretty much impossible to diagnose your issue if you don't post a complete, coherent repro; it could be a syntax error, it could be a missing
#include
, who knows..?Here's a working demonstration; hopefully you can use it as a reference to figure out what's wrong with your code:
As an aside, I think using a tuple with purely homogeneous types is strange; personally, I'd change the grammar's synthesized attribute to
boost::array<bool, 4>
.EDIT (in response to OP's edit): There's good news and bad news and more good news.
Here's the good news: Boost.Fusion has functionality to do exactly what you want to do with minimal code:
boost::fusion::fused<>
. This will take a callable type (including free-function pointers and member-function pointers) that takes multiple arguments and wrap that callable type in a functor that takes a Fusion sequence; when this functor is invoked, it takes the Fusion sequence and unpacks it, forwarding the individual elements of the tuple to the wrapped callable type as separate arguments.So, given the grammar I already posted and the following:
boost::spirit::qi::parse()
can be called like so:Here's the bad news: Boost.Fusion's callable type wrappers rely on the TR1/C++11
result_of
protocol, while Boost.Phoenix v2 implements the Boost.Lambdaresult_of
protocol – these are not compatible. As a result, you must unpack the tuple elements yourself:Yuck! But, there's more good news: Boost.Phoenix v3 is going to be released in Boost 1.47, and it implements the TR1/C++11
result_of
protocol. Consequently, starting with Boost 1.47 you'll be able to useboost::fusion::fused<>
and save yourself some tedious boilerplate.作为一般说明,我建议阅读 Spirit 网站上有关属性处理的文章此处< /a>.这些构成了随库分发的在线文档的一个很好的附录。
As a general note, I'd suggest to read the articles about attribute handling on the Spirit website here. These constitute a nice addendum to the online docs as distributed with the library.
qi::_bool >> 的属性qi::_bool>> qi::_bool>> qi::_bool
是std::vector
或任何其他 stl 容器,如参考文献中所述:http://www.boost.org/doc/libs/1_46_0/libs/spirit/doc/html/spirit/qi/quick_reference/compound_attribute_rules.html。表的第一行就是这种情况:)
The attribute of
qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool
isstd::vector<bool>
or any other stl container, as it's described in the reference: http://www.boost.org/doc/libs/1_46_0/libs/spirit/doc/html/spirit/qi/quick_reference/compound_attribute_rules.html.The first row of the table is the case :)