Повысьте :: Spirit :: Ци.Как превратить встроенные выражения парсера в автономные грамматики и как распаковать сгенерированные ими кортежи? - PullRequest
9 голосов
/ 28 мая 2011

Я использую QI и Phoenix, и я хочу написать небольшую грамматику, которая возвращает 4 bools, которые должны использоваться в качестве аргументов для вызова функции внутри семантического действия.

У меня есть несколько функций, которые нуждаются в этих вещах, и до сих пор я использовал этот подход:

( 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)].

Это означает, что моя проблема заключается в распаковке аргументов, соответствующих сигнатуре вызываемой функции, поскольку количество аргументов различается на уровне сигнатуры (один кортеж из четырех bools против четырех bools самостоятельно).

Можно ли распаковать, используя заполнители phoenix напрямую, вместо того, чтобы писать обертки, которые переводят кортежи в отдельные аргументы для моих существующих функций, которые нуждаются в них отдельно? Если это так, какой будет синтаксис для этого? В конце концов, встроенная версия, такая как ( qi::_bool >> qi::_bool >> qi::_bool >> qi::_bool), прекрасно работает, когда 'распаковывается' spirit::_1 - spirit::_4, заполнителями.

Мне кажется, что эта версия также возвращает кортеж и каким-то образом не распаковывается с помощью вышеуказанного подхода, в отличие от грамматики, которая ее возвращает.

Как мне с этим справиться?

Ответы [ 3 ]

12 голосов
/ 28 мая 2011

Невозможно диагностировать вашу проблему, если вы не публикуете полное, связное воспроизведение;это может быть синтаксическая ошибка, это может быть отсутствующий #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>.


РЕДАКТИРОВАТЬ (в ответ на редактирование ОП): есть хорошие новости, плохие новости и еще больше хороших новостей.

Вот хорошие новости: 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<> и сэкономить на некоторых утомительных шаблонах.

3 голосов
/ 28 мая 2011

В качестве общего замечания я бы предложил прочитать статьи об обработке атрибутов на веб-сайте Spirit здесь .Это хорошее дополнение к онлайн-документам, распространяемым вместе с библиотекой.

0 голосов
/ 28 мая 2011

Атрибут 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.

Первая строка таблицы - это регистр:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...