Преобразование рукописного цикла в вызов библиотеки std - PullRequest
0 голосов
/ 02 октября 2018

Недавно я смотрел выступление Шона Родителя о приправе C ++ от 2013 года. Если я его правильно понял, он говорит, что вы можете исключить из своего кода почти все (все?) Рукописные циклы.У меня вопрос как этого добиться?Давайте рассмотрим следующий код:

class ProgressDialog
{
  //interesting part of that class
  void SetPosition(int position);
  bool IsCancelRequested();
  void SetHeader(const std::string& status);
}
void foo( const std::vector<std::string>& v)
{
  ProgressDialog dlg;
  long position = 0;
  for( const auto& s : v)
  {
    ++position;
    dlg.SetPosition(position);
    dlg.SetHeader("Processing"+ s);
    DoSomethingThatTakesSomeTime(s);
    if(dlg.IsCancelRequested()) break;
  }
}

Есть ли способ реорганизовать рукописный цикл?

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

Общий случай

Если я правильно его понял, он говорит, что вы можете исключить из своего кода почти все (все?) Рукописные циклы.

ХорошоДа и нет - очевидно, это зависит от того, какие петли вы пишете.Я полагаю, что он слегка преувеличил, чтобы подчеркнуть тот факт, что много циклов не являются действительно необходимыми, и, возможно, лучше реорганизовать их в применение стандартных "алгоритмов" библиотеки.

Кроме того, по существу, любой циклможет быть заменен на std::for_each, но это на самом деле не считается, поскольку он скрывает только явное управление прогрессией цикла и все еще является «просто циклом».

Ваш конкретный пример

ВВ вашем случае итерация цикла работает, когда регистрирует / сообщает о каждой итерации выполняемой работы и готова принять запрос на прерывание.Вы могли бы написать вариант std::for_each с этим дополнительным аспектом функциональности, затем использовать его для фактической работы в качестве особого случая, например,

namespace with_progress_dialog {

template< class InputIt, class UnaryFunction >
void for_each(
    InputIt          first, 
    InputIt          last,
    UnaryFunction    function,
    std::string_view message_prefix = "Processing item " )
{
    ProgressDialog progress_dialog;
    for (position_t position = 0; first != last; ++first, ++position) 
    {
        progress_dialog.SetPosition(position);
        progress_dialog.SetHeader(message_prefix + position);
        function(*first);
        if ( progress_dialog.IsCancelRequested() ) { break; }
    }
}
} 

, а затем вызвать

// ... etc. etc. ...
with_progress_dialog::for_each(
    std::begin(v), std::end(v),
    &DoSomethingThatTakesSomeTime);
* 1022.* Теперь, это, конечно, слишком обобщение.Но я предполагаю, что у вас есть другие случаи, когда вы открываете диалог прогресса, который постепенно обновляется.Так что, возможно, адаптируйте ваше обобщение соответствующим образом.Или - может быть, вы можете сохранить какое-то состояние на уровне окна, когда выполняете тяжелую работу, и сделать так, чтобы другой поток отслеживал это и открывал диалоговое окно, когда это необходимо, или отображал индикацию в строке состояния.Еще один вариант, возможно, запустить этот диалог в co-rountine (но это довольно умозрительно, но я не уверен, что это хорошая идея).

Примечание: это не очень хорошоИдея, и потенциально небезопасная, просто продолжать и печатать строки, над которыми вы работаете, - которые, вероятно, могут возникать из входных данных, которые вы не контролируете.Вы должны удостовериться, что они не слишком длинные, и рассмотреть их дезинфекцию.В моем коде я печатаю только индекс («позиция»).

0 голосов
/ 02 октября 2018

Я не уверен, добавляет ли это какой-либо ясности, но это попытка реорганизовать концепцию цикла и раннего прерывания.

#include <string>
#include <vector>
#include <ciso646>

struct ProgressDialog
{
  //interesting part of that class
  void SetPosition(int position);
  bool IsCancelRequested();
  void SetHeader(const std::string& status);
};
void DoSomethingThatTakesSomeTime(std::string const&);

// iterate over a container, calling func with the current value.
// if func returns false, cease iterating immediately.
// return true if terminated early
// note: func must return false if it wishes early termination,
// otherwise true
template<class Cont, class F> 
auto for_each_while(Cont&& container, F&& func)
{
    for(auto&& s : container)
        if (not func(s)) 
            return true;
    return false;
}

void foo( const std::vector<std::string>& v)
{
    auto update_dialog = 
    [position = 0, dlg = ProgressDialog()](auto&& s) mutable
    {
        ++position;
        dlg.SetPosition(position);
        dlg.SetHeader("Processing"+ s);
        DoSomethingThatTakesSomeTime(s);
        return !dlg.IsCancelRequested();
    };
    for_each_while(v, update_dialog);
}

А вот некоторые злоупотребления библиотекой std, которые достигают того жевещь.

Я настоятельно рекомендую вам не делать этого, поскольку для обычного читателя неясно, что происходит!

void foo( const std::vector<std::string>& v)
{
    int position = 0;
    auto dlg = ProgressDialog();

    std::find_if(begin(v), end(v), 
                 [&](auto&& s)
    {
        ++position;
        dlg.SetPosition(position);
        dlg.SetHeader("Processing"+ s);
        DoSomethingThatTakesSomeTime(s);
        return dlg.IsCancelRequested();
    });
}
...