捕捉通过模板参数包提供的异常类型



是否可以编写一个模板函数并将异常类型列表传递给它来处理?

我想实现这样的东西

template<class Exc1, Exc2>
auto foo()
{
try {
bar();
} catch (const Exc1& e) {
log_error(e);
} catch (const Exc2& e) {
log_error(e);
}
}

但是要处理的异常类型的数量是可变的。

下面的代码可以满足我的需要,但当然不能编译

template<class... Exceptions>
auto foo()
{
try {
bar();
} catch (const Exceptions& e...) {
log_error(e);
}
}

为什么我需要这个?我正在编写一个通用的"重试"机制,应该调用提供可调用的,重试几次,以防从列表中抛出异常(但让所有其他异常冒泡)。目前我通过提供两个可调用对象来绕过这个问题:目标函数和异常处理程序:

template<class Function, class ExceptionHandler>
auto retry(Function function, ExceptionHandler handler)
{
for (auto i = 0; i < 3; ++i) {
try {
return function();
} catch (...) {
handler();
}
}
return function();
}
auto f = [] {
// do something and possibly throw an exception
};
auto h = [] {
try {
throw;
} catch (const Exc1& e) {
log_error(e);
} catch (consy Exc2& e) {
log_error(e);
}
};
retry(f, h);

上面的代码和我期望的一样,但是我希望有一个更优雅的解决方案。

你可以尝试使用递归

template<class Exception,  class... Exceptions>
auto foo()
{
if constexpr (sizeof...(Exceptions) == 0)
{
try {
bar();
} catch (const Exception& e) {
log_error(e);
}
}
else
{
try {
foo<Exceptions...>();
} catch (const Exception& e) {
log_error(e);
}
}
}

godbolt的工作示例

如果您希望避免递归,并且您从std::exception派生所有异常,您可以使用dynamic_cast的可变扩展来匹配异常类型:

#include <stdexcept>
#include <iostream>
template<class...Exceptions, class Handler>
void 
try_handle(Handler handler, std::exception& e)
{
bool handled = false;
auto check = [&](auto* pexception)
{
if (!pexception || handled)
return;
handled = true;
handler(*pexception);
};
( check(dynamic_cast<Exceptions*>(&e)), ... );
if (!handled)
std::rethrow_exception(std::current_exception());
}
auto 
test(std::exception_ptr ep)
{
auto handler1 = [](auto& ex)
{
std::cout << "handler 1: " << ex.what() << 'n';
};
auto handler2 = [](auto& ex)
{
std::cout << "handler 2: " << ex.what() << 'n';
};
try
{
try
{
std::rethrow_exception(ep);
}
catch(std::exception& e)
{
try_handle<std::invalid_argument, std::logic_error>(handler1, e);
}
}
catch(std::exception& e)
{
try_handle<std::runtime_error, std::logic_error>(handler2, e);
}
}
int main()
{
test(std::make_exception_ptr(std::invalid_argument("argument")));
test(std::make_exception_ptr(std::runtime_error("runtime")));
test(std::make_exception_ptr(std::logic_error("logic")));
}

预期输出:

handler 1: argument
handler 2: runtime
handler 1: logic

最新更新