Boost :: Spirit :: Qi如何将内联的解析器表达式转换为独立的语法,以及如何解压缩由它们生成的元组?

问题描述

| 我正在使用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>()
>
{
    typedef boost::fusion::vector4<bool,bool> attribute_type;

    FourBools() : base_type(start_)
    {
        using boost::spirit::bool_;

        start_
            =   \"4bools:\"
            >> 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> a)
{

}
int main()
{
    namespace phx = boost::phoenix;
    namespace spirit = boost::spirit;

    std::string const input(\"4bools: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占位符解压缩,而不是编写将元组转换为需要它们分开的现有函数的单独参数的包装器? 如果是的话,其语法将是什么? 毕竟,当由
spirit::_1 - spirit::_4,
占位符“解压”时,像
( qi::_bool >>  qi::_bool >>  qi::_bool >>  qi::_bool)
这样的内联版本可以正常工作。 在我看来,这个版本似乎也返回了一个元组,并且以某种方式无法用上述方法打包,这与返回一个语法的语法不同。 我该如何处理?     

解决方法

        如果您没有发布完整,连贯的副本,则几乎不可能诊断出问题。可能是语法错误,可能是缺少的“ 6”,谁知道..? 这是一个可行的演示;希望您可以将其用作参考来找出您的代码出了什么问题:
///// 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>()
>
{
    typedef boost::fusion::vector4<bool,bool> attribute_type;

    FourBools() : base_type(start_)
    {
        using boost::spirit::bool_;

        start_
            =   \"4bools:\"
                >> 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>

typedef FourBools::attribute_type attr_t;

struct verify_same
{
    explicit verify_same(attr_t const& expected) : expected_(expected) { }

    void verify(attr_t const& actual) const
    {
        using boost::fusion::at_c;

        std::cout << std::boolalpha
            << \"same as expected: \" << (actual == expected_)
            << \"\\nactual values: \"
            << at_c<0>(actual) << \' \'
            << at_c<1>(actual) << \' \'
            << at_c<2>(actual) << \' \'
            << at_c<3>(actual) << \'\\n\';
    }

private:
    attr_t expected_;
};

int main()
{
    namespace phx = boost::phoenix;
    namespace spirit = boost::spirit;

    std::string const input(\"4bools:true,true,false;\");
    verify_same const vs(attr_t(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(&verify_same::verify,phx::cref(vs),spirit::_1)]
    );
    if (!success)
        std::cout << \"parse() failed\\n\";
    else if (first != last)
        std::cout << \"didn\'t consume all input\\n\";
    std::cout.flush();
}
顺便说一句,我认为使用具有纯同质类型的元组是很奇怪的。我个人将语法的综合属性更改为
boost::array<bool,4>
。 编辑(响应OP的编辑):有好消息,坏消息和更多好消息。 这是个好消息:Boost.Fusion具有功能,只需很少的代码即可完成您想做的事情:
boost::fusion::fused<>
。这将采用带有多个参数的可调用类型(包括自由函数指针和成员函数指针),并将该可调用类型包装在采用Fusion序列的函子中;调用此仿函数时,它将采用Fusion序列并将其解压缩,将元组的各个元素作为单独的参数转发给包装的可调用类型。 因此,鉴于我已经发布的语法以及以下内容:
#include <string>
#include <ios>
#include <iostream>
#include <boost/fusion/include/at_c.hpp>
#include <boost/fusion/include/make_fused.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/qi_action.hpp>
#include <boost/spirit/include/qi_parse.hpp>

typedef FourBools::attribute_type attr_t;

void free_func_taking_tuple(attr_t const& tup)
{
    using boost::fusion::at_c;

    std::cout << std::boolalpha
        << \"inside free_func_taking_tuple() :: \"
        << at_c<0>(tup) << \' \'
        << at_c<1>(tup) << \' \'
        << at_c<2>(tup) << \' \'
        << at_c<3>(tup) << \'\\n\';
}

void free_func_taking_bools(
    bool const a,bool const b,bool const c,bool const d
)
{
    std::cout << std::boolalpha
        << \"inside free_func_taking_bools() :: \"
        << a << \' \'
        << b << \' \'
        << c << \' \'
        << d << \'\\n\';
}
boost::spirit::qi::parse()
可以这样称呼:
namespace phx = boost::phoenix;
namespace spirit = boost::spirit;
using boost::fusion::make_fused;

// calls free_func_taking_tuple,nothing new here
spirit::qi::parse(
    first,fourBools[phx::bind(free_func_taking_tuple,spirit::_1)]
);

// calls free_func_taking_bools,using boost::fusion::fused<> to unpack the tuple
// into separate arguments
spirit::qi::parse(
    first,fourBools[phx::bind(make_fused(&free_func_taking_bools),spirit::_1)]
);
这是一个坏消息:Boost.Fusion的可调用类型包装程序依赖于TR1 / C ++ 11
result_of
协议,而Boost.Phoenix v2实现了Boost.Lambda
result_of
协议–它们不兼容。结果,您必须自己解压缩元组元素:
namespace phx = boost::phoenix;
namespace spirit = boost::spirit;

spirit::qi::parse(
    first,fourBools[phx::bind(
        free_func_taking_bools,phx::at_c<0>(spirit::_1),phx::at_c<1>(spirit::_1),phx::at_c<2>(spirit::_1),phx::at_c<3>(spirit::_1)
    )]
);
!但是,还有更多好消息:Boost.Phoenix v3将在Boost 1.47中发布,并实现TR1 / C ++ 11
result_of
协议。因此,从Boost 1.47开始,您将可以使用ѭ9并节省一些繁琐的样板。     ,        作为一般说明,我建议在此处阅读Spirit网站上有关属性处理的文章。这些构成了随库一起分发的在线文档的很好的附录。     ,        
qi::_bool >> qi::_bool >>  qi::_bool >> qi::_bool
的属性是
std::vector<bool>
或任何其他stl容器,如参考中所述:http://www.boost.org/doc/libs/1_46_0/libs/spirit/doc/html/spirit/qi/ quick_reference / compound_attribute_rules.html。 该表的第一行就是这种情况:)