#include <iostream>
#include <stdexcept>
static std::string msg{"Not Implemented"};
class NotImplemented : public std::logic_error
{
public:
NotImplemented(const std::string& s, int value)
: std::logic_error{msg} {
_what = msg + ": " + s + " (" + std::to_string(value) + ")";
}
const char* what() const noexcept {
return _what.c_str();
}
private:
std::string _what;
};
int main(int argc, char *argv[])
{
try {
throw NotImplemented("C++11 example");
}
catch (NotImplemented& ni) {
std::cout << "NotImplemented: " << ni.what() << std::endl;
}
catch (std::exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
}
Contents
Why use exceptions?
- Using exceptions for error handling makes your code simpler, cleaner, and less likely to miss errors [1].
- Handling errors in a constructor just can’t be done right without exceptions [1].
- The overhead of using exceptions is only a few percent (say, 3%) compared to no error handling [1].
What to throw?
- You can throw anything you like, but generally, it’s best to throw objects, not built-ins [1].
- If possible, throw instances of classes that derive (ultimately) from the std::exception class [1].
- This gives users the option of catching most things via std::exception [1][6].
- Throwing a pointer to a dynamically allocated object is never a good idea [3].
- “std::exception” is a type, and you cannot throw a type. Instead use: “throw std::exception();” [3]
Exception class hierarchy
The point of having this hierarchy is to give user the opportunity to use the full power of C++ exception handling mechanism. Since ‘catch’ clause can catch polymorphic exceptions, the user can write ‘catch’ clauses that can catch exception types from a specific subtree of the exception hierarchy. [4]
Designing a useful exception class hierarchy (that would let you catch only the exception types you are interested in at each point of your code) is a non-trivial task. What you see the in standard C++ library (see [2]) is one possible approach, offered to you by the authors of the language. [4]
Difference between std::exception, std::runtime_error and std::logic_error
std::exception
is the class whose only purpose is to serve as the base class in the exception hierarchy. It has no other uses. [4]
std::runtime_error
is a more specialized class, descending from std::exception
, intended to be thrown in case of various runtime errors. It has a dual purpose. It can be thrown by itself, or it can serve as a base class to various even more specialized types of runtime error exceptions. [4]
The class std::runtime_error
defines the type of objects thrown as exceptions to report errors presumably detectable only when the program executes. [5]
Exceptions derived from std::runtime_error include std::range_error
, std::overflow_error
and regex_error
[2].
The class std::logic_error
defines the type of objects thrown as exceptions to report errors presumably detectable before the program executes, such as violations of logical preconditions or class invariants. [5]
Exceptions derived from std::logic_error
include std::invalid_argument
, std::domain_error
and std::out_of_range
[2].
So it might be confusing when to use std::runtime_error
and when std::logic_error
. The first includes std::range_error
(that is, situations where a result of a computation cannot be represented by the destination type), the second std::out_of_range
(attempt to access elements out of defined range).
So, runtime errors result from runtime conditions outside of the control of the programmer (like I/O errors and invalid user input). Logic errors are problems that result from internal failures to adhere to program invariants [5].
/*
* --- Do not mix exceptions with error codes [6].
*
* ---- Structure or class?
* I would recommend using structs as plain-old-data (POD) structures without any class-like features,
* and using classes as aggregate data structures with private data and member functions [7].
*
* == When to throw?
* Exceptions are used to signal errors that cannot be handled locally [1].
* Do not use exceptions as simply another way to return a value from a function [1].
*
* == What to catch?
* You can catch by value, reference or pointer, but unless there’s a good reason not to, catch by reference [1].
* The 'catch' clause can catch polymorphic exceptions [4].
*
* == When to catch?
* Ask yourself this question for each try block: “Why am I using a try block here?” [1]
* If the exception is actually handled, the try block is probably good [1].
* If it is (for example) to close a file and rethrow the exception, use RAII to perform the action in the destructor [1].
* It it is to repackage the exception, consider a better hierarchy of exception objects [1].
*/
#include <iostream>
#include <exception>
using namespace std;
class myexception: public exception
{
virtual const char* what() const throw()
{
return "My exception happened";
}
} myex;
void simpleType()
{
try
{
throw 20;
}
catch (int e) { cout << "int exception Nr. " << e << '\n'; }
catch (char param) { cout << "char exception"; }
catch (...) { cout << "default exception"; }
}
void hierarchy()
{
try
{
//throw myex;
throw myexception();
}
catch (exception& e)
{
cout << e.what() << '\n';
}
// warning: exception of type ‘myexception’ will be caught by earlier handler for ‘std::exception’
// error: ‘virtual const char* myexception::what() const’ is private within this context
// catch (myexception& e) { cout << "MyException:" << e.what() << '\n'; }
}
int main()
{
simpleType();
hierarchy();
return 0;
}
References:
- ISO C++ Exceptions FAQ
- CPP Reference std::exception
- https://stackoverflow.com/questions/10948316/throw-new-stdexception-vs-throw-stdexception
- Stack Overflow std::runtime_error vs std::exception
- Stack Overflow std::runtime_error vs std::logic_error
- https://www.acodersjourney.com/top-15-c-exception-handling-mistakes-avoid/
- https://stackoverflow.com/questions/54585/when-should-you-use-a-class-vs-a-struct-in-c
- C++ Coding Standards, 101 Rules, Guidelines, and Best Practives; Herb Sutter, Andrei Alexandrescu; item 68 til 75.