Can I / Should I use std :: exception for regular error handling?

I am going to launch this new project in C ++, and I think that this is an illogical way of handling errors. Now Iโ€™m not going to start throwing and catching exceptions, and quite possibly I will never throw exceptions at all, but I thought - even for regular error handling, why roll back my own / copy -paste class to describe errors / status when I could just use std::exception and its child classes (or maybe std::optional<std::exception> )?

 using Status = std::optional<std::exception>; Status somethingThatMayFail(int x); 

Does anyone / any project work this way? Is this a funny idea or a bit squeaky?

+8
c ++ idioms idiomatic error-handling
source share
3 answers

I do not think that you should create exceptions if you are not really going to throw them. I would recommend a return type of bool or enum. The goal will be much clearer, someone will read your code, and they will be faster. However, if you throw an exception, someone else will come and think that they can throw an exception and cause the whole system to crash.

C ++ exceptions play an important role in resource management, triggering destructors and all that (RAII). Using them in any other way can hurt performance and (more importantly) confuse the saint from anyone who tries to save the code later.

However, you can do what you want with a state reporting class that has nothing to do with std :: exception. People do too much for fast code when they donโ€™t need it. If enumerating the state is not enough and you need to return more information, then the state reporting class will work. If this makes it easier to read the code, then go.

Just don't call it an exception unless you throw it away.

+3
source share

I think that performance alone may be problematic. Consider the following code:

 #include <iostream> #include <chrono> #include <ctime> #include <stdexcept> #include <boost/optional.hpp> int return_code_foo(int i) { if(i < 10) return -1; return 0; } std::logic_error return_exception_foo(int i) { if(i < 10) return std::logic_error("error"); return std::logic_error(""); } boost::optional<std::logic_error> return_optional_foo(int i) { if(i < 10) return boost::optional<std::logic_error>(std::logic_error("error")); return boost::optional<std::logic_error>(); } void exception_foo(int i) { if(i < 10) throw std::logic_error("error"); } int main() { std::chrono::time_point<std::chrono::system_clock> start, end; start = std::chrono::system_clock::now(); for(size_t i = 11; i < 9999999; ++i) return_code_foo(i); end = std::chrono::system_clock::now(); std::cout << "code elapsed time: " << (end - start).count() << "s\n"; start = std::chrono::system_clock::now(); for(size_t i = 11; i < 9999999; ++i) return_exception_foo(i); end = std::chrono::system_clock::now(); std::cout << "exception elapsed time: " << (end - start).count() << "s\n"; start = std::chrono::system_clock::now(); for(size_t i = 11; i < 9999999; ++i) return_optional_foo(i); end = std::chrono::system_clock::now(); std::cout << "optional elapsed time: " << (end - start).count() << "s\n"; start = std::chrono::system_clock::now(); for(size_t i = 11; i < 9999999; ++i) exception_foo(i); end = std::chrono::system_clock::now(); std::cout << "exception elapsed time: " << (end - start).count() << "s\n"; return 0; } 

On my CentOS, using gcc 4.7, it is confined to:

 [amit@amit tmp]$ ./a.out code elapsed time: 39893s exception elapsed time: 466762s optional elapsed time: 215282s exception elapsed time: 38436s 

in the vanilla settings and:

 [amit@amit tmp]$ ./a.out code elapsed time: 0s exception elapsed time: 238985s optional elapsed time: 33595s exception elapsed time: 24350 

in the settings -O2.

PS I personally would use exceptions / stacking because of the belief that this is a fundamental part of C +, possibly as @vsoftco said above.

+2
source share

To answer your question, this is not a ridiculous idea, and you can use std::exception to regularly handle errors; with some reservations.

Using std::exception as a result of a function

Let's say that a function can exit several error states:

 std::exception f( int i ) { if (i > 10) return std::out_of_range( "Index is out of range" ); if ( can_do_f() ) return unexpected_operation( "Can't do f in the current state" ); return do_the_job(); } 

How can you handle this with std::exception or optional? When the function returns, a copy of the exception will be created, containing only the std::exception and rejecting the specification of the actual error; leaving you the only information that "yes, something went wrong ...". The behavior will be the same as returning a logical or optional expected result type, if any.

Using std::exception_ptr to save specifications

Another solution would be to apply the same approach as in std :: prom , i.e. return std::exception_ptr . There you can return either nothing or an exception, while maintaining the actual error data. Recovering the actual type of error can be difficult.

Return an error or result in the same object

Finally, another option would be to use the Expected<T> clause and its implementation . There you can return a value or error in one object and process this error if you want (by checking for errors or with regular exception handling), with some features for a function that does not return a value (more on Stack Overflow or this blog ).

How to choose

My personal opinion on this is that if you intend to use exceptions, use them the way they were designed, eventually using some additional tools, such as Expected<T> , to simplify it. Otherwise, if you cannot use standard exception handling, then move on to a solution that has established itself as a classic system of error codes.

0
source share

All Articles