Is it possible to write/wrap the exception handling components (try,catch) in different class?

梦想与她 提交于 2019-12-04 06:42:45
Praetorian

What you're asking for is possible, but I don't think it's very useful. First let's implement a mechanism to accept a callable object, and its associated arguments, which we'll invoke in the destructor of 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)...);
}

This makes use of the C++14 std::integer_sequence; if that's not available on your implementation there are several answers on SO that show how to implement it yourself (this one for instance).

To use it, create an exception_wrapper object, and your function will be invoked when the destructor executes.

make_exception_wrapper(function);

Live demo


Now, I don't think this is useful because in general you should only catch exceptions if your code is able to handle them, and continue operating normally. Otherwise let them propagate to the top level where you might want to install a handler so it allows you to exit the program gracefully.

Given that, it's unlikely that there'll be a common approach to handling all exceptions thrown by your code, which greatly reduces the utility of exception_wrapper as implemented. You could modify it to take another callable argument, the exception handler that will be passed the std::exception object that was caught, which makes the class a little more generic.

Additionally, invoking the function in the destructor means you cannot pass the return value, if any, back to the caller. This can be fixed by invoking the function within exception_wrapper::operator() instead, but that then adds the wrinkle of what to return in the case an exception is indeed thrown, and you've suppressed it.

Finally, do not write code that throws types that are not derived from std::exception. This makes your code unidiomatic, and if you do want to handle the exception, you'll need to litter the code with several catch statements, like you have in your example.

It is possible using 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());
    }
}

You can also achieve this without std::exception_ptr, just put throw; in place of std::rethrow_exception(e);, hoping this function will be invoked only if there is an active exception being handled (otherwise your program will be terminate()'ed):

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!