cppreference documentation https://en.cppreference.com/w/cpp/algorithm/for_each says that:
- If execution of a function invoked as part of the algorithm throws an exception and ExecutionPolicy is one of the three standard policies, std::terminate is called. For any other ExecutionPolicy, the behavior is implementation-defined.
I interpret that this means that I cannot, out-of-the-box, throw from the for_each
passed function and expect to catch the excetion or some information related to it.
The reason I was expecting to use exceptions was so that I could partially undo (revert) the changes made in the for_each
call.
(Maybe there is a better algorithm for that).
However, just by chance I found a historical version of for_each
which is documented to have a different, more interesting behavior:
http://man.hubwiz.com/docset/C.docset/Contents/Resources/Documents/output/en/cpp/algorithm/for_each.html
- if policy is std::parallel_vector_execution_policy, std::terminate is called
- if policy is std::sequential_execution_policy or std::parallel_execution_policy, the algorithm exits with an std::exception_list containing all uncaught exceptions. If there was only one uncaught exception, the algorithm may rethrow it without wrapping in std::exception_list. It is unspecified how much work the algorithm will perform before returning after the first exception was encountered.
Which seems to imply that instead of terminate
ing, there is the possibility of actually using exceptions.
So, why std::exception_list
was eliminated?, was it too controversial, too complicated, too (memory) costly?
Even if I agree with the logic, I really don't have any other option because parallel for_each
returns void
(instead of the UnaryFunction back, which is also surprising).
Therefore,
this std::exception_list
protocol seem to me in a necessary component to undo a failed-to-complete for_each
instruction.
Is it reasonable to expect that some new custom policy, e.g. par_with_failed_list
will appear somewhere allowing for undo
ing.
More context: This pattern of undoing a failed loop is used in construction of containers. I want to implement a custom (parallel/sequencial) uninitialized_value_construct_n
which "undo's" (destroy) the initilized objects when (any of the unsequenced) constructions fail.
EDIT1: On a second though, it might be possible to pass a captured variable in the lambda to a function parameter. This variable could be a shared concurrent data that can store the exceptions as they happen (as a exception_list). I wonder if this has been done already.
EDIT2: I found an implementation of exception_list
in 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
std::exception_list
added a lot of complexity to the specification and implementation of parallel algorithms without much corresponding gain.
As a user, you can handle this case in the functor:
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()});
}});
The exceptions
list will now contain a list of pointers to the elements where an exception was thrown and the thrown exceptions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With