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

It's about wrapping the exception handling logic in some class. When writing C ++ code, many times we need to catch many types / options of exception depending on which client throws. This forces us to write similar types of code (many times) in catch ().

In the example below, I wrote a function () that can throw an exception in many possible forms.

I wanted to know whether it is possible to write / wrap such logic in the form of a class so that the end user should write the same types of code right away ?. Does it make sense or make sense?

#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 have thought this way, and below is 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 } }; 

An object of this class can be created in main () in this way.

 int main() { exceptionwrapper obj(function); //here execptionwrapper destructor would take care about calling all type of catch } 
+5
source share
2 answers

What you ask is possible, but I do not think it is very useful. First, give the opportunity to implement a mechanism for accepting the called object and the arguments associated with it, which we will call in the exception_wrapper destructor.

 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 uses C ++ 14 std::integer_sequence ; if this is not available for your implementation, there are several answers to SO that show how to implement it yourself ( this one ).

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

 make_exception_wrapper(function); 

Live demo


Now I do not think this is useful, because in the general case you should catch only exceptions if your code is able to handle them and continue to work normally. Otherwise, they will propagate to the upper level, where you may want to install a handler so that it can gracefully exit the program.

Given this, it is unlikely that there will be a general approach to handling all exceptions thrown by your code, which greatly reduces the usefulness of exception_wrapper , as implemented. You can modify it to accept another argument being called, an exception handler that will be passed the std::exception object that has been caught, which makes the class more universal.

In addition, calling a function in the destructor means that you cannot pass the return value, if any, back to the caller. This can be fixed by calling the function inside exception_wrapper::operator() instead, but then it adds a wrinkle to what should be returned if the exception is really thrown and you suppressed it.

Finally, do not write code that yields types that are not derived from std::exception . This makes your code unified, and if you want to handle an exception, you will need to lure the code with several catch statements, as was the case in your example.

+3
source

It is possible to use 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; instead of std::rethrow_exception(e); , hoping that this function will be called only if the active exception is handled (otherwise your program will 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(); } 

Another live demo link.

+12
source

Source: https://habr.com/ru/post/1201461/


All Articles