Отказ от проанализированного результата после семантического действия - PullRequest
1 голос
/ 26 апреля 2019

В Boost.Spirit можно читать из потока в std::vector, просто выполнив:

#include<vector>
#include<boost/spirit/include/qi.hpp>
namespace sqi = boost::spirit::qi;
int main(){
        std::string const v_str = "AA BB CC";
        std::vector<std::string> v;
        auto it = begin(v_str);
        bool r = sqi::phrase_parse(it, end(v_str), 
                    (*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, v);
        assert( v.size() == 3  and v[2] == "CC" );
}

Однако бывает так, что я заранее знаю количество элементов из-за формата ввода, и я должен иметь возможность сохранить место в векторе. Например, если строка ввода «3 AA BB CC», можно заранее выделить три элемента.

Вопрос в том, как передать эту дополнительную информацию в вектор и оптимизировать более позднюю push_back (например, избежать перераспределений).

В начале я попытался проанализировать целое число, связав семантическое действие с ним, где выполняется reserve.

        std::string const v_str = "3 AA BB CC";
        std::vector<std::string> v;
        auto it = begin(v_str);
        bool r = sqi::phrase_parse(it, end(v_str), 
             sqi::int_[([&](int i){v.reserve(i);})] >> 
                (*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, v);

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

Другим обходным решением может быть добавление еще одного аргумента к функции phrase_parse, но это кажется излишним.

Итак, как мне проанализировать что-то в Boost.Spirit и выполнить только семантическое действие без отправки результата в переменную приемника?

Даже если это можно сделать, я не совсем уверен, что это правильный способ сделать это.

Ответы [ 3 ]

0 голосов
/ 26 апреля 2019

Хорошо, мне кажется, что мне пришлось деконструировать легкие возможности Духа и преобразовать все в семантическое действие, что создало другую проблему на пути (например, lexeme[+char_] отображается на std::vector<char> вместо исключенного std::string.

{
    std::string const v_str = "AA BB CC";
    std::vector<std::string> v;
    auto it = begin(v_str);
    bool r = sqi::phrase_parse(it, end(v_str), 
        (*(sqi::lexeme[(+sqi::char_("A-Z"))][([&](auto&& s){v.emplace_back(begin(s), end(s));})])), sqi::space);
    assert( v.size() == 3);
    assert( v[2] == "CC" );
}
{
    std::string const v_str = "3 AA BB CC";
    std::vector<std::string> v;
    auto it = begin(v_str);
    bool r = sqi::phrase_parse(it, end(v_str), 
        sqi::int_[([&](int i){v.reserve(i);})] >> 
            (*(sqi::lexeme[(+sqi::char_("A-Z"))][([&](auto&& s){v.emplace_back(begin(s), end(s));})])), sqi::space);
    assert( v.size() == 3 );
    assert( v[2] == "CC" );
}

Поскольку это изменяет последний аргумент phrase_parse, я мог бы также поставить пустышку int.

0 голосов
/ 26 апреля 2019

Благодаря ссылкам, на которые мне указали @sehe и @drus, и обнаружив около qi::omit, я понимаю, что могу связать семантическое действие и затем опустить результат.

Формат, который я должен обработать, является избыточным (размер избыточен с количеством элементов), поэтому я должен семантически опустить что-то в любом случае.

    using namespace sqi;
    std::string const v_str = "3 AA BB CC";
    {
        std::vector<std::string> v;
        auto it = begin(v_str);
        bool r = sqi::phrase_parse(
            it, end(v_str), 
            omit[int_] >> *lexeme[+(char_-' ')],
            space, v
        );
        assert( v.size() == 3 and v[2] == "CC" );
    }

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

    {
        std::vector<std::string> v;
        auto it = begin(v_str);
        bool r = sqi::phrase_parse(
            it, end(v_str), 
            omit[int_[([&](int n){v.reserve(n);})]] >> *lexeme[+(char_-' ')],
            space, v
        );
        assert( v.size() == 3 and v[2] == "CC" );
    }

Я согласен с тем, что семантические действия являются злом, но, на мой взгляд, только когда они меняют состояние объектов-приемников. Можно утверждать, что reserve не меняет состояние вектора.

Фактически, таким образом я могу оптимизировать использование памяти с помощью reserve, а также выполнение синтаксического анализатора, используя repeat вместо неограниченного клина *. (очевидно, repeat может быть более эффективным) .

    {
        std::vector<std::string> v;
        auto it = begin(v_str);
        int n;
        bool r = sqi::phrase_parse(
            it, end(v_str), 
            omit[int_[([&](int nn){v.reserve(n = nn);})]] >> repeat(phx::ref(n))[lexeme[+(char_-' ')]],
            space, v
        );
        assert( n == v.size() and v.size() == 3 and v[2] == "CC" );
    }

(unsing phx::ref является основополагающим, потому что оценка n должна быть отложена)

0 голосов
/ 26 апреля 2019

Вы можете создать поддельный вектор, который будет учитываться при вставке и разборе одного и того же текста дважды:

#include<vector>
#include<boost/spirit/include/qi.hpp>
namespace sqi = boost::spirit::qi;
struct fake_vector
{
    typedef std::string value_type;
    fake_vector() : counter(0) {}
    std::size_t end() const {return 0;};
    void insert(std::size_t, std::string){ ++counter; }

    std::size_t counter;
};
int main(){
        std::string const v_str = "AA BB CC";
        auto it = begin(v_str);
        fake_vector fv;
        bool r = sqi::phrase_parse(it, end(v_str), (*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, fv);
        assert(fv.counter == 3);
        std::vector<std::string> v;
        v.reserve(fv.counter);
        it = begin(v_str);
        r = sqi::phrase_parse(it, end(v_str), (*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, v);
        assert( v.size() == 3  and v[2] == "CC" );
}
...