Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Exception handling

Tags: exception
Hi all, today I am going to write about a renowned topic, namely, Exception handling. Exception is a mean for indication of a deviation from the normal execution flow of the program, though it can always be expected and handled appropriately.
    To indicate such a deviation we need to generate an exception. For this purpose the throw keyword is used. It needs an object as an argument which will represent the exception. This could be either an object of a built-in or a user-defined type:

throw int();
throw MyClass();

As soon as throw statement is executed the execution leaves the current scope and starts to seek a “corresponding” handler. A handler is introduced by means of catchkeyword which also needs the type of the exception to be handled:

catch (int)
{
       // .. handle the exceptional situation
}

catch (MyClass)
{
       // .. handle the exceptional situation
}

To handle the code with catch statements the dangerous code (a piece of code that may throw an exception) should be placed in a try block:

try {
       // dangerous code with possible exception
}

So the final scenario is as follows:

try {
       // .. some code
       throw objectOfMyClass;
       // .. maybe some more code
}
catch (MyClass)
{
       // .. handle the exceptional situation
}

All these things are to provide a little understanding of what goes on. Now let’s get deeper and see everything in more details. The try block usually does not contain the throw statement directly. Instead, it contains a function call and that function includes either a throw statement or another function call with throw statement. I.e., the throw statement can be nested any number of levels.

As soon as an exception is thrown something known as stack unwindinghappens, i.e. all the local objects (not static and not extern) on the stack in the current stack frame are destroyed (destructors are called). If the throw statement is immediately in the try block, then only the objects declared in the block will be destroyed, otherwise, if throw is in a function body, first the destructors of all local object allocated on the stack in that function body are called, then only the ones in try block. After that the exception object is copied to be passed to a handler body and the execution continues from the “corresponding” handler. Where the copied object is kept is unspecified. If no handler is found for the exception, std::termintate() function is called from <exception> header. The same happens (std::terminate() call) if an exception is thrown while unwinding the stack. That's why it is not recommended to have unsafe code in destructors, because if a local object of that type is created its destructor will be called during stack unwinding and the second throw statement will bring to program termination. We will get back to std::terminate() soon, but now let’s understand what a “corresponding” handler is.

Usually, an exception handler not only specifies the type of the exception to be caught, but also introduces a variable which will the exception object be assigned to. This is done because exceptions as a rule contain information about what went wrong, why, or any other user-defined info, at least exceptions also are class objects, so anyone can define his/her own exception classes. So the handler looks like this:

catch (MyClass ex) {
       // handle the exception or
       // inform about failure in a friendly manner
}

Actually it is not a good practice to use the type itself, as it will copy the exception object into the locally introduced variable. So to avoid this overhead, we can catch the exception by reference similar to passing arguments to functions. Here we go:

catch (const MyClass& ex) {
       // handle the exception or
       // inform about failure in a friendly manner
}

The constkeyword may be omitted, but rarely one would want to modify the thrown exception object. To catch an exception one need not to specify the exact type of the thrown object. The same exception can be caught by a handler which expects an object of parent types, for example:

try {
       throw Derived();
}
catch (const Base& ex) {
       // ... handle here
}

Note, that this is possible only in case of public inheritance. If the Base is a protected or a private base of Derived, then the handler will not catch the exception. Now if we expect several types of exceptions in a hierarchy we need to handle the most specific (derived) ones first:

catch (const Derived& ex) {
       // exception of type Derived is handled here
}
catch (const Base& ex) {
       // exception of type Base is handled here
}

If we change the order of handlers, then the second one (expecting exceptions of type Derived) will never be reached, as the first one will handle exceptions of both Base and Derived types. Modern compilers should at least warn about this.

So, as you get, the number of handlers is not limited. We already know that the handlers with exact type or with base types are “corresponding”. To learn about other possible correspondence, see the clause 15.3.3 of C++ standard specification.

Exception handlers can also be nested. For example if one handler catches an exception of type T1 and throws another one of type T2, there could be a handler of type T2 inside the former one, as follows:

catch (const T1& ex) {
       // try to handle T1
       // if could not throw T2
       try {
              throw T2();
       } catch (const T2& ex) {
              // handle T2 here
       }
}

If we want to handle some exceptions which may raise from constructor body we can add try-catch blocks there. But if we want to handle the exceptions coming from the constructor initialization list, then the syntax a changes a little. Here is what it looks like:


class MyClass : public Base
{
private:
       SomeType mem;

public:
       MyClass(const SomeType& arg)
              try : Base(), mem(arg)
       {
              // Any exception thrown from initialization list
              // or from c'tor body can be handled below.
       }
       catch (...)
       {
              // A handler should follow the c'tor body.
       }
};

It is possible, that a piece of code tries to handle the exception, but if it does not manage to, it rethrows the exception, as if this piece of code never existed. Other handlers in different parts of system may try to handle the exception and so on. In order to achieve this effect we again refer to the same throw keyword, but this time without any parameters:

catch (const T1& ex) {
       // ... try to handle the exception
       // if unable just rethrow
       throw;
}

What if we rethrow just throwing the caught exception object (“throw ex;”)? “throw;” rethrows the originally thrown object, which means:
  • it does not copy the exception object one more time as opposed to “throw ex;
  • it does not change the type of exception. What we caught is not necessarily what was thrown, so if we rethrow what we caught, the type of exception may be changed. See the example below:

try {
       throw Derived();
}
catch (const Base& ex) {
       // The exception rethrown below is copied
       // and it has type Base, not Derived
       throw ex;
}

If we replace the last line in handler body with “throw;” the original exception of type Derived will be rethrown.

The keyword throwhas another application either. It is exception specification. This is a way to specify what types of exceptions may a function throw:

void f1() throw(); // does not throw at all
void f2() throw(int); // may throw only int
void f3() throw(Base, int); // may throw Base and int

If the function throws an exception of not specified type, std::unexpected() is called which by default calls std::terminate(). A little later about this as I already promised. We will not get much into the throw specifications as they are deprecated in C++11. For similar purpose the noexcept keyword has been introduced in C++11, which can be used either as an operator or a specifier. You can find the details on the web, but here is a brief explanation.
  • noexcept specifier specifies whether the function throws exceptions or not. It can be used with or without expression argument. If an expression is evaluated to true, then the function should not throw exceptions. Otherwise, it may. noexcept without expression argument is equivalent to noexcept(true). If a function marked with noexcept throws an exception, std::terminate() is called.
  • noexcept operator is always used with a constant expression, and performs a check whether the expression is specified as noexcept. Note that this is a compile-time check.


The specifier and operator are often used together in generic codes, for example:

template <classContainer>
int my_func(constContainer& c, int index) noexcept(noexcept(Container::operator[]))
{
       return c[index];
}

If the Container’s operator[] is marked with noexcept then the operator will return true, and my_func will also be noexcept, otherwise it will not.

The time has come to talk about std::terminate() and std::unexpected(). As stated above std::unexpected() is called when a function throws an exception inconsistent with its throw specification (remember this is deprecated in C++11). By default std::unexpected()in its turn calls std::terminate(). And std::terminate() by default invokes std::abort() which stops the program execution without calling destructors for any constructed object. Nevertheless, we are free to substitute this functions with our owns. There are two typedefs in <exception> header:

typedef void(*unexpected_handler)();
typedef void(*terminate_handler)();

These are just pointers to functions accepting no arguments and returning nothing. And there are two functions which make it possible to override what std::unexpected()and std::terminate() call with our own functions:

unexpected_handler set_unexpected(unexpected_handler f) throw();
terminate_handler set_terminate (terminate_handler f) throw();

In C++11 both functions are marked with noexcept specifier instead of empty exception specification. Both functions return the currently set handlers. This is good for keeping the current handlers to temporarily substitute them with our owns and then restore again. Though in C++11 it is possible just to call std::get_unexpected() or std::get_terminate() to retrieve current handlers.

There are many exception classes in standard library <exception> header. The most general one is std::exceptionitself. And all the others derive from it. It has one public virtual function, which returns a human-readable message about the exception:

virtual const char* what() const;

So you can call what() on any exception from standard library. There is also a set of predefined exception classes in <stdexcept> header.

That’s it. There are many things that you’ll learn experimenting on exceptions. I cannot remember and write here all the things, but I hope this post gives a good understanding of how the exception handling mechanism works and how you can modify it. Thanks for your precious time.


This post first appeared on C++: Learning From Experience, please read the originial post: here

Share the post

Exception handling

×

Subscribe to C++: Learning From Experience

Get updates delivered right to your inbox!

Thank you for your subscription

×