如何:捕获输入函数异常的可变参数包装函数

问题描述 投票:0回答:4

我正在尝试创建一个可以传递其他函数的函数,它将捕获任何错误,但否则只是返回函数的返回值。这是我尝试过的:

#include <iostream>
using namespace std;

int fun(int input)
{
    return input;
}

template <typename F, typename...Args>
static auto HandledCall(const F& function, Args...args)
-> decltype(function(...args))
{
    try
    {
        return function(...args);
    }
    catch(...)
    {
        return NULL;
    }
}

int main() {
    std::cout << HandledCall(fun,1) << std::endl; // this should return 1
    std::cout << HandledCall(fun,-1) << std::endl; // this should return 0      
    return 0;
}

希望意图比较明确;我希望

HandledCall
能够接收任何类型的函数,并返回其返回值(只要
NULL
在发生错误时可以隐式转换为该值)。然而,当我尝试编译上面的代码时,我收到了这些类型的错误;

prog.cpp:10:78: 错误:“...”标记之前需要主表达式 静态自动 HandledCall(const F& 函数, Args...args) -> decltype(函数(...args))

显然我没有正确地执行这个可变参数模板...有什么建议吗?

c++ templates c++11 variadic-templates function-templates
4个回答
4
投票

函数调用的返回类型可以使用

std::result_of
确定。

template<typename F, typename... Args>
typename std::result_of<F(Args...)>::type
    HandledCall(F&& func, Args&&... args)
{
    using result_type = typename std::result_of<F(Args...)>::type;
    try {
        return std::forward<F>(func)(std::forward<Args>(args)...);
    } catch(...) {
        return result_type();
    }
}

现场演示


3
投票

有这样的事吗?

#include <iostream>
using namespace std;

int fun(int input)
{
    return input;
}

template <typename T> struct ReturnType;

template<class Ret, class... Args>
struct ReturnType<Ret(Args...)> 
{
   typedef Ret type;
};


template <typename F, typename...Args>
static auto HandledCall(const F& function, Args...args) -> typename ReturnType<F>::type
{
    try
    {
        return function(args...);
    }
    catch(...)
    {
        return typename ReturnType<F>::type{0};
    }
}

int main() {
    std::cout << HandledCall(fun,1) << std::endl; // this should return 1
    std::cout << HandledCall(fun,-1) << std::endl; // this should return 0      
    return 0;
}

更新

HandledCall
的改进版本(感谢Mankarse):

template <typename F, typename...Args>
static auto HandledCall(const F& function, Args&&...args) -> typename ReturnType<F>::type
{
    try
    {
        return function(std::forward<Args>(args)...);
    }
    catch(...)
    {
        return typename ReturnType<F>::type{0};
    }
}

1
投票

这是一个基于 @Praetorian 提出的解决方案的版本,但它也适用于具有

void
返回类型的函数。其他答案无法处理这种情况的原因是
void
类型的对象的显式实例化。

template<typename T> 
T default_value(){return {};}

template<>
void default_value<void>(){}

template<typename F, typename... Args>
typename std::result_of<F(Args...)>::type
  HandledCall(F&& func, Args&&... args)
{
  try {
      return std::forward<F>(func)(std::forward<Args>(args)...);
  } catch(...) {
      return default_value<typename std::result_of<F(Args...)>::type>();
  }
}

这样做的原因是因为标准允许在具有 void 返回类型的函数中显式返回 void 值。


0
投票

包装的函数通常在编译时就已知,因此我建议通过如下所示的方式来利用它(> = c ++ 17):

#include <cstdlib>
#include <stdexcept>
#include <type_traits>
#include <utility>

template <auto F, typename... Args>
auto nonThrowingFunWrapper(Args... args)
{
    using result_type = std::invoke_result_t<decltype(F), Args...>;
    constexpr std::integral_constant<decltype(F), F> fun_object;
    try
    {
        return fun_object(std::forward<Args>(args)...);
    }
    catch (...)
    {
        std::abort();
        return result_type();
    }
}

int foo(int bar)
{
    throw std::runtime_error("foo failed");
    return bar;
}

int main()
{
    return nonThrowingFunWrapper<foo>(43);
}
© www.soinside.com 2019 - 2024. All rights reserved.