There are situations in runtime, when program runs in exceptional way.
This could be handle in many different ways: using boolean return values,
assertions or exceptions.



Handling exceptional situations: the old C way
==============================================


struct record { ... };

struct record rec;
extern int errno;  /* stdio standard error code */
int myerrno;       /* my custom error code */

FILE *fp;

if ( NULL == (fp = fopen( "fname", "r")) ) /* try to open the file */
{
    fprintf( stderr, "can't open file %s\n", "fname");
    fprintf( stderr, "reason: %s\n", strerror(errno)); /* perror(NULL) */
    myerrno = 1;
}
else if ( ! fseek( fp, n*sizeof(rec), SEEK_SET) ) /* positioning to "n"th record */
{
    fprintf( stderr, "can't find record %d\n", n);
    myerrno = 2;
}
else if ( 1 != fread( &r, sizeof(r), 1, fp) ) /* try to read a record */
{
    fprintf( stderr, "can't read record\n");
    myerrno = 3;
}
else   /* everything was succesfull up to now */
{
 ...
}




Handling exceptional situations: Asserts
========================================


// run-time assert
#include <cassert>

int main()
{
    ...
    assert( ptr );
    ...
}


// compile time assert since C++11
// sample from http://en.cppreference.com/w/cpp/language/static_assert

#include <type_traits>

template <typename T>
void swap(T &x, T &y)
{
    static_assert( std::is_copy_constructible<T>::value, "Swap requires copying" );
    static_assert( std::is_nothrow_move_constructible<T>::value &&
                   std::is_nothrow_move_assignable<T>::value, "Swap may throw" );
   auto tmp = x;
   x = y;
   y = tmp;
}





Goals of exception handling
===========================


- type-safe transmisson of arbitrary data from throw-point to handler
- no extra code/space/time penalty if not used
- every exceptionis cought by the appropriate handler
- groupping of exceptions
- ok in multithreaded environment
- cooperation with other languages (like C)

But the extra cost in code and runtime most cases unavoidable.
Scott Meyers states that declaring exceptions, even if it never
used costs extra 10% both in extra code and extra runtime.



Since PL/I programming languages have technique for handling exceptional
situations. Most famous is the setjmp/longjmp in C language. Here is an
example, how it is working.


#include <setjmp.h>
#include <stdio.h>

jmp_buf x;

void f()
{
    longjmp(x,5);
}

int main()
{
    int i = 0;

    if ( (i = setjmp(x)) == 0 )
    {
        f();
    }
    else
    {
        switch( i )
        {
        case  1:
        case  2:
        default: fprintf( stdout, "error code = %d\n", i); break;
        }
    }
    return 0;
}



Components of exception handling
================================


Here we describe the main components of exceptions: the try block,
the catch handlers, and the throw expression.


The main components:

- try block
- catch handlers
- throw expression


    try
    {
        f();
        // ...
    }
    catch (T1 e1) { /* handler for T1 */ }
    catch (T2 e2) { /* handler for T2 */ }
    catch (T3 e3) { /* handler for T3 */ }



    void f()
    {
        //...
        T e;
        throw e;    /* throws exception of type T */

        // or:
        throw T();  /* throws default value of T */
    }


A hadler H triggers on exception E if:

- H has same type as E
- H is unambigous base type of E
- H and E pointers or references and 1 or 2 holds



Exception hierarchies
=====================


In object-oriented programming languages the main technique for expressing
specialization and generalization is the inheritance hierarchy. Inheritance
in exceptions are particularry important for groupping exceptions in catch
handlers.


try {
    ...
}
catch( Der1 d1 ) { ... }
catch( Der2 d2 ) { ... }
catch( Der3 d3 ) { ... }
catch( Base b )  { ... }


// example1:

class net_error { ... };
class file_error { ... };

class nfs_error : public net_error, public file_error { ... };


void f()
{
    try
    {
        ...
    }
    catch( nfs_error nfs ) { ... }
    catch( file_error fe ) { ... }
    catch( net_error ne )  { ... }
}




// example2:



#ifndef MATRIX_H
#define MATRIX_H

#include <string>
#include <sstream>
#include <stdexcept>

struct matrixError
{
    matrixError( std::string r) : reason(r) { }
    std::string reason;
};

struct indexError : public matrixError, public std::out_of_range
{
    indexError( int i, const char *r="Bad index") :
                  matrixError(r), out_of_range(r), index(i)
    {
        std::ostringstream os;
	os << index;
	reason += ", index = ";
	reason += os.str();
    }
/* BAD
    const char *what() const throw()
    {
        std::ostringstream os;
        os << std::out_of_range::what();
        os << ", index = " << index;
        return os.str().c_str();
    }
 */
    const char *what() const throw()
    {
        return reason.c_str();
    }
    virtual ~indexError() nothrow;
    int index;
};
struct rowIndexError : public indexError
{
    rowIndexError(int i) : indexError( i, "Bad row index") { }
};
struct colIndexError : public indexError
{
    colIndexError(int i) : indexError( i, "Bad col index") { }
};

template <class T>
class matrix
{
public:
       matrix( int i, int j );
       matrix( const matrix &other);
       ~matrix();
    matrix operator=( const matrix &other);

    int rows() const { return x; }
    int cols() const { return y; }

    T& at( int i, int j);       // throw(indexError);
    T  at( int i, int j) const; // throw(indexError);
    T& operator()( int i, int j);
    T  operator()( int i, int j) const;

    matrix operator+=( const matrix &other);

private:
    int  x;
    int  y;
    T   *v;
    void copy( const matrix &other);
    void check( int i, int j) const; // throw(indexError);
};


template <class T>
void matrix<T>::copy( const matrix &other)
{
    x = other.x;
    y = other.y;
    v = 0;
    v = new T[x*y];
    for ( int i = 0; i < x*y; ++i )
        v[i] = other.v[i];
    // possible memory leak at v
}

template <class T>
void matrix<T>::check( int i, int j) const // throw( indexError )
{
    if ( i  < 0  ||  i >= x )
        throw rowIndexError(i);
    if ( j  < 0  ||  j >= y )
        throw colIndexError(j);
}

template <class T>
T& matrix<T>::at( int i, int j) // throw(indexError)
{
    check(i,j);
    return operator()(i,j);
}

template <class T>
T matrix<T>::at( int i, int j) const // throw( indexError)
{
    check(i,j);
    return operator()(i,j);
}

template <class T>
T& matrix<T>::operator()( int i, int j)
{
    return v[i*y + j];
}

template <class T>
T matrix<T>::operator() ( int i, int j) const
{
    return v[i*y + j];
}

#endif /* MATRIX_H */




Exceptions in the standard library

Comparing to Java language C++ has only a few standard exception types.
User programs can derive their own exception classes to express specific
exceptions. Here we present the standard exceptions.



class exception {};                        // <exception>
class bad_cast   : public exception {};    // dynamic_cast
class bad_typeid : public exception {};    // typeid(0)
class bad_exception : public exception {}; // unexpected()
// class ios_base::failure : public exception {}; // before C++11 
class bad_weak_ptr : public exception {};  // C++11 weak_ptr -> shared_ptr
class bad_function_call : public exception {};// C++11 std::function::operator()
class bad_alloc  : public exception {};    // new  <new>
class bad_array_new_length : bad_alloc {}  // C++11, new T[-1]

class runtime_error : public exception {};  // math. computation

class range_error     : public runtime_error {}; // floating point ovf or unf
class overflow_error  : public runtime_error {}; // integer overflow INT_MAX+1
class underflow_error : public runtime_error {}; // integer underflow INT_MIN-1
class system_error    : public runtime_error {}; // e.g. std::thread constr.
class ios_base::failure : public system_error {}; // C++11 unexpected() <ios>

class logic_error : public exception {};

class domain_error : public logic_error {}; // domain error, liek std::sqrt(-1)
class invalid_argument : public logic_error {}; // bitset char != 0 or 1
class length_error : public logic_error {}; // length str.resize(-1)
class out_of_range : public logic_error {}; // bad index in cont. or string
class future_error : public logic_error {}; // C++11: duplicate get/set


namespace std
{
    class exception
    {
    public:
        exception();
        exception( const exception& other);
        virtual ~exception();
        exception& operator=( const exception& other);
        virtual const char *what() const;
    private:
        // ...
    };
}