Boost::Spirit::Qi。如何将内联解析器表达式转换为独立的语法,并如何拆分它们生成的元组?

9
我正在使用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 占位符“解包”后,可以正常工作。

这使我觉得这个版本也返回了一个元组,并且可以通过上述方法进行拆分,而不像返回一个元组的语法。

我该如何处理这个问题?

3个回答

12

如果您没有发布完整的、连贯的重现步骤,那么诊断您的问题几乎是不可能的;原因可能是语法错误,也可能是缺少#include等等。以下是一个可行的演示,希望您可以将其用作参考来找出代码中的问题:

///// 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>

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,true,false;");
    verify_same const vs(attr_t(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(&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, last,
    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, last,
    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, last,
    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 开始,您将能够使用 boost::fusion::fused<> 并节省一些冗长的样板代码。


像这样的注释可能不被看好,但我必须发表一下我对这种代码的兴奋之情。它让我感到非常自卑,但也很有动力,因为我甚至不知道列出的一半是什么,而我以为我已经变得更好了。无论如何,如果在彻底分析您的帖子后仍然无法正常工作,明天我会提供一个可重现的例子。 - Erius
@Erius :-] 如果你最终发布了一个可重现的代码, 请在我的回答下发表评论,这样我就会在我的SO收件箱中收到消息,否则我可能不会看到你的编辑。 - ildjarn

3

一般来说,我建议您阅读Spirit网站这里关于属性处理的文章。这些文章是与该库一起分发的在线文档的良好补充。


收藏了。我会好好研究一下,也谢谢你。 - Erius

0

刚刚尝试了一下,还是无法编译通过,除非不使用qi::_1 - qi::_4从向量中提取值。 - Erius
bool_ >> bool_ >> bool_ >> bool_ 的属性确实是 fusion::vector<bool, bool, bool, bool>;Spirit只是碰巧允许使用 some_container<bool>,因为元组完全由同类类型组成。这种行为在Spirit文档中被称为“属性折叠”。 - ildjarn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接