Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

This is about wrapping the exception handling logic in some sort of class. While writing c++ code, many time we need to catch many type/variants of exception depending on what client throw. This lead us to write similar types of code(many times) in catch() clause.

In below sample example, I have written the function(), which can throw exception in the many possible form.

I wanted to know is it possible to write/wrap such logic in the form of class so that end user would have to write similar types of code at once place?. Does it make any sense or it has any meaning?

#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;
}

So far I thought in this way and below is the pseudo logic.

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
}

};

The object of this class can be instantiated in the main() in this way.

int main() {
 exceptionwrapper obj(function);
 //here execptionwrapper destructor would take care about calling all type of catch
}
like image 793
Mantosh Kumar Avatar asked Aug 30 '14 05:08

Mantosh Kumar


People also ask

Is multiple wrapping methods needed in exception handling?

If you do not wrap exceptions, but instead pass them on by declaring your methods to throw them, you may end up with top level methods that declare many different exceptions. Declaring all these exceptions in each method back up the call stack becomes tedious.

Should you wrap everything try catch?

Learning programming: Why should I not wrap every block in “try”-“catch”? A statement is either necessary, or not. Try-catch is sometimes useful, but often doesn't add value. Anything that doesn't add value is code bloat.

How would you wrap a caught exception in a new ArithmeticException exception?

Wrapping exception simply involves setting the InnerException property of the new exception to propagate up the call stack, to the caught exception. Doing this maintains the exception call stack information. You can see that the caught exception was the ArithmeticException and it had the defined error message.

Should you wrap exceptions?

No, generally, you should not do that: this may mask the real exception, which may indicate programming issues in your code. For example, if the code inside the try / catch has a bad line that causes an array index out of bound error, your code would catch that, too, and throw a custom exception for it.


2 Answers

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.

like image 196
Piotr Skotnicki Avatar answered Oct 11 '22 14:10

Piotr Skotnicki


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.

like image 43
Praetorian Avatar answered Oct 11 '22 15:10

Praetorian