是否可以在不同的类中编写/包装异常处理组件(try,catch)?

时间:2014-08-30 05:26:06

标签: c++ c++11 exception-handling raii

这是关于在某种类中包装异常处理逻辑。在编写c ++时 代码,很多时候我们需要捕获许多类型/变体的异常,具体取决于客户端抛出的内容。这导致我们在catch()子句中编写类似类型的代码(多次)。

在下面的示例示例中,我编写了函数(),它可以以多种可能的形式抛出异常。

我想知道是否有可能以类的形式编写/包装这样的逻辑,以便最终用户必须一次编写类似类型的代码? 它有意义还是有意义?

#include<vector>
#include<string>
#include<exception>
#include<iostream>

// this function can throw std::exception, std::string, int or unhandled
void function() {
  std::vector<int> x{1,2,3,4,5};
  auto val = x.at(x.size()); //throw out-of-range error
}

int main() {
try { function(); } 
catch(std::exception& e) { std::cout<<e.what()<<std::endl; }
catch(std::string& msg)  { std::cout<<msg<<std::endl; }
catch(int i)         { std::cout<<i<<std::endl; }
catch(...)       { std::cout<<"Unhandled Exception"<<std::endl; }
return 0;
}

到目前为止,我以这种方式思考,下面是伪逻辑

class exceptionwrapper{
exceptionwrapper(function pointer* fp) {
 // functions which would be executing inside try
}
~exceptionwrapper() {
// all catch() clause can be written over here
// or some other member function of this class
}

};

这个类的对象可以用这种方式在 main()中实例化。

int main() {
 exceptionwrapper obj(function);
 //here execptionwrapper destructor would take care about calling all type of catch
}

2 个答案:

答案 0 :(得分:13)

可以使用std::exception_ptr

Live demo link.

#include <iostream>
#include <exception>
#include <stdexcept>

void universal_exception_handler(std::exception_ptr e)
{
    try
    {
        std::rethrow_exception(e);
    }
    catch (const std::logic_error& e)
    {
        std::cout << "logic_error" << std::endl;
    }
    catch (const std::runtime_error& e)
    {
        std::cout << "runtime_error" << std::endl;
    }
}

void foo()
{
    throw std::logic_error{""};
}

void bar()
{
    throw std::runtime_error{""};
}

int main()
{
    try
    {
        foo();
    }
    catch (...)
    {
        universal_exception_handler(std::current_exception());
    }

    try
    {
        bar();
    }
    catch (...)
    {
        universal_exception_handler(std::current_exception());
    }
}

您也可以在没有std::exception_ptr的情况下实现此目的,只需将throw;代替std::rethrow_exception(e);,希望只有在有效时才会调用此功能处理异常(否则您的程序将terminate()'编辑):

void universal_exception_handler()
{
    try
    {
        throw;
    }
    catch (const std::logic_error& e)
    {
        std::cout << "logic_error" << std::endl;
    }
    catch (const std::runtime_error& e)
    {
        std::cout << "runtime_error" << std::endl;
    }
}

try
{
    foo();
}
catch (...)
{
    universal_exception_handler();
}

Yet another live demo link.

答案 1 :(得分:3)

您可以提出的要求是可能的,但我认为它非常有用。首先,让我们实现一个机制来接受一个可调用对象及其相关参数,我们将在exception_wrapper的析构函数中调用它。

template<typename Func, typename... Args>
struct exception_wrapper
{
    exception_wrapper(Func f, Args... args) 
    : f_(std::move(f))
    , args_(std::make_tuple(std::move(args)...))
    {}

    ~exception_wrapper() 
    {
        try {
            invoke();
        } catch(std::exception const& e) {
            std::cerr << "Caught exception: " << e.what() << std::endl;
        } catch(...) {
            std::cerr << "Caught unknown exception" << std::endl;
        }
    }

    template<std::size_t... I>
    void apply(std::index_sequence<I...>)
    {
        f_(std::get<I>(args_)...);
    }

    void invoke()
    {
        apply(std::index_sequence_for<Args...>());
    }

    Func f_;
    std::tuple<Args...> args_;
};

template<typename Func, typename... Args>
auto make_exception_wrapper(Func&& f, Args&&... args)
{
    return exception_wrapper<Func, Args...>(
        std::forward<Func>(f), std::forward<Args>(args)...);
}

这使用了C ++ 14 std::integer_sequence;如果你的实现没有这个,那么有几个答案可以显示如何自己实现它(例如this one)。

要使用它,请创建一个exception_wrapper对象,并在析构函数执行时调用您的函数。

make_exception_wrapper(function);

Live demo


现在,我不认为这很有用,因为一般来说,如果您的代码能够处理它们并继续正常运行,您应该只捕获异常。否则,让它们传播到您可能想要安装处理程序的顶层,以便它可以优雅地退出程序。

鉴于此,处理代码抛出的所有异常的常用方法不太可能,这大大降低了exception_wrapper实现的效用。您可以修改它以获取另一个可调用的参数,该异常处理程序将传递被捕获的std::exception对象,这使得该类更通用。

此外,在析构函数中调用函数意味着您无法将返回值(如果有)传递回调用者。这可以通过调用exception_wrapper::operator()中的函数来修复,但是这会在确实抛出异常的情况下添加返回的内容的皱纹,并且您已经抑制了它。

最后,编写抛出不是从std::exception派生的类型的代码。这使得您的代码非常简单,如果您确实想要处理异常,那么您需要使用几个catch语句来丢弃代码,就像您在示例中所做的那样。