Использование for_each при наличии исключений?станд :: exception_list - PullRequest
7 голосов
/ 13 марта 2019

справочная документация https://en.cppreference.com/w/cpp/algorithm/for_each говорит что:

  • Если при выполнении функции, вызванной как часть алгоритма, возникает исключение, и ExecutionPolicy является одной из трех стандартных политик , std :: terminate называется . Для любого другого ExecutionPolicy поведение определяется реализацией.

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

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

Однако, случайно, я нашел историческую версию for_each, которая, как задокументировано, имеет другое, более интересное поведение:

http://man.hubwiz.com/docset/C.docset/Contents/Resources/Documents/output/en/cpp/algorithm/for_each.html

  • если политика имеет тип std :: parallel_vector_execution_policy, вызывается std :: terminate
  • если для политики задано std :: sequential_execution_policy или std :: parallel_execution_policy, алгоритм завершает работу со списком std :: exception_list, содержащим все необработанные исключения . Если было только одно неперехваченное исключение, алгоритм может перебросить его без включения в std :: exception_list. Не определено, сколько работы выполнит алгоритм перед возвратом после первого исключения.

Что, по-видимому, означает, что вместо terminate ing существует возможность фактически использовать исключения.

Итак, почему std::exception_list была устранена ?, была слишком противоречивой, слишком сложно, слишком (память) дорого? 1043 *

Даже если я согласен с логикой, у меня действительно нет никакой другой опции, потому что параллельная for_each возвращает void (вместо обратной UnaryFunction, что также удивительно). Следовательно, этот протокол std::exception_list кажется мне необходимым компонентом для отмены невыполненной инструкции for_each.

Разумно ли ожидать появления какой-то новой пользовательской политики, например, par_with_failed_list появится где-то с учетом undo ing.


Больше контекста : Этот шаблон отмены неудачного цикла используется при создании контейнеров. Я хочу реализовать пользовательский (параллельный / последовательный) uninitialized_value_construct_n, который "отменяет" (уничтожает) инициализированные объекты при сбое (любого из неупорядоченных) конструкций.


EDIT1 : Хотя на секунду может быть возможно передать захваченную переменную в лямбда-параметре в параметр функции. Эта переменная может быть общими параллельными данными, которые могут хранить исключения по мере их возникновения (как список_исключений). Интересно, было ли это уже сделано?


EDIT2 : Я нашел реализацию exception_list в HPX,
https://github.com/STEllAR-GROUP/hpx/blob/master/hpx/exception_list.hpp
https://github.com/STEllAR-GROUP/hpx/blob/master/src/exception_list.cpp

1 Ответ

1 голос
/ 26 марта 2019

std::exception_list добавил много сложности к спецификации и реализации параллельных алгоритмов без особого соответствующего усиления.

Как пользователь, вы можете обрабатывать этот случай в функторе:

struct exception_info{
    ElementType* element;
    std::exception_ptr exception;
};
std::vector<exception_info> exceptions;
std::mutex exceptions_mutex;

std::vector<ElementType> range=...;

std::for_each(std::execution::par,range.begin(),range.end(),[&](ElementType& element){
    try{ do_stuff(element); }
    catch(...){
        std::lock_guard guard(exceptions_mutex);
        exceptions.push_back({&element,std::current_exception()});
    }});

Список exceptions теперь будет содержать список указателей на элементы, где было сгенерировано исключение, и сгенерированные исключения.

...