Exceptions

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

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:

  1. ISO C++ Exceptions FAQ
  2. CPP Reference std::exception
  3. https://stackoverflow.com/questions/10948316/throw-new-stdexception-vs-throw-stdexception
  4. Stack Overflow std::runtime_error vs std::exception
  5. Stack Overflow std::runtime_error vs std::logic_error
  6. https://www.acodersjourney.com/top-15-c-exception-handling-mistakes-avoid/
  7. https://stackoverflow.com/questions/54585/when-should-you-use-a-class-vs-a-struct-in-c
  8. C++ Coding Standards, 101 Rules, Guidelines, and Best Practives; Herb Sutter, Andrei Alexandrescu; item 68 til 75.