for_each 在出现异常时的用法? std::exception_list
Usage of for_each in the presence of exceptions? std::exception_list
cppreference 文档 https://en.cppreference.com/w/cpp/algorithm/for_each
说:
- 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.
我认为这意味着我不能开箱即用地从 for_each
传递的函数中抛出并期望捕获异常或与之相关的一些信息。
我期望使用异常的原因是我可以部分撤消(还原)在 for_each
调用中所做的更改。
(也许有更好的算法)。
然而,我偶然发现了 for_each
的一个历史版本,它被记录为具有不同的、更有趣的行为:
- 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.
这似乎意味着可以实际使用异常而不是 terminate
ing。
所以,为什么std::exception_list
被淘汰了?,是不是争议太大,太复杂,太(内存)费?
即使我同意逻辑,我真的没有任何其他选择,因为并行for_each
returns void
(而不是后面的UnaryFunction,这也是令人惊讶的).
所以,
在我看来,此 std::exception_list
协议是撤消未能完成的 for_each
指令的必要组成部分。
期望一些新的自定义策略是否合理,例如par_with_failed_list
会出现在允许 undo
ing 的地方。
更多上下文:这种撤消失败循环的模式用于构建容器。我想实现一个自定义 (parallel/sequencial) uninitialized_value_construct_n
当(任何未排序的)构造失败时 "undo's" (销毁)初始化对象。
EDIT1:尽管如此,也许可以将 lambda 中捕获的变量传递给函数参数。
这个变量可以是一个共享的并发数据,可以在异常发生时存储它们(作为 exception_list)。
我想知道这是否已经完成了。
EDIT2:我在 HPX 中找到了 exception_list
的实现,
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
为并行算法的规范和实现增加了很多复杂性,却没有多少相应的收益。
作为用户,您可以在仿函数中处理这种情况:
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
列表现在将包含指向抛出异常的元素和抛出的异常的指针列表。
cppreference 文档 https://en.cppreference.com/w/cpp/algorithm/for_each 说:
- 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.
我认为这意味着我不能开箱即用地从 for_each
传递的函数中抛出并期望捕获异常或与之相关的一些信息。
我期望使用异常的原因是我可以部分撤消(还原)在 for_each
调用中所做的更改。
(也许有更好的算法)。
然而,我偶然发现了 for_each
的一个历史版本,它被记录为具有不同的、更有趣的行为:
- 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.
这似乎意味着可以实际使用异常而不是 terminate
ing。
所以,为什么std::exception_list
被淘汰了?,是不是争议太大,太复杂,太(内存)费?
即使我同意逻辑,我真的没有任何其他选择,因为并行for_each
returns void
(而不是后面的UnaryFunction,这也是令人惊讶的).
所以,
在我看来,此 std::exception_list
协议是撤消未能完成的 for_each
指令的必要组成部分。
期望一些新的自定义策略是否合理,例如par_with_failed_list
会出现在允许 undo
ing 的地方。
更多上下文:这种撤消失败循环的模式用于构建容器。我想实现一个自定义 (parallel/sequencial) uninitialized_value_construct_n
当(任何未排序的)构造失败时 "undo's" (销毁)初始化对象。
EDIT1:尽管如此,也许可以将 lambda 中捕获的变量传递给函数参数。 这个变量可以是一个共享的并发数据,可以在异常发生时存储它们(作为 exception_list)。 我想知道这是否已经完成了。
EDIT2:我在 HPX 中找到了 exception_list
的实现,
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
为并行算法的规范和实现增加了很多复杂性,却没有多少相应的收益。
作为用户,您可以在仿函数中处理这种情况:
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
列表现在将包含指向抛出异常的元素和抛出的异常的指针列表。