14. Standard C++ Library Diagnostics

14.1 Introduction

The Diagnostics Library contains new components that C++ programs may use to detect and report error conditions.

The following header files contain components for reporting several kinds of exceptional conditions, documenting program assertions, and a global variable for error number codes:

Exception classes <stdexcept>
Assertions <cassert>
Error numbers <cerrno>

14.2 Exception Class Hierarchies

Using a common base class allows the use of one handler for many related exceptions. For example, a set of exception objects for memory allocation problems could be derived from the MemoryException base class such as out memory or ZeroAlloc. A single catch (MemoryException) handler would catch all memory exceptions including new ones. Virtual functions allow derived exception classes to use internal information that doesn’t exist in the base class.

14.2.1 Using class hierarchy to define your own exception classes

The following example demonstrates how to use class hierarchies to define your own exception classes

// Compile Options. /GX
/**************************************************************
This program demonstrates how to use Class Hierarchies to define
your own exception classes.
It does not use a new new operator function from the Standard
Template Library. The intention is to demonstrate how to use
class hierarchies to define your own exception classes.
**************************************************************/

#include <iostream>
class MemoryException {
    public:
        virtual void DisplayMessage() = 0;
};

class OutOfMemory : public MemoryException {
    public:
        void DisplayMessage();
};

void OutOfMemory::DisplayMessage() {
    cout << " Out of Memory!" << endl;
}

class BadPointer : public MemoryException {
    private:
        void * p;
        virtual void DisplayMessage();
};

void BadPointer :: DisplayMessage () {
    cout << "Invalid pointer: " << endl;
}

const size_t szBuf = 99999999;
int* iBuf;
char* cBuf;
void InitApp();

void main () {
    try {
        InitApp();
    }

    catch (MemoryException & ex) {
        ex.DisplayMessage();
    }
}

void InitApp() {
    try {
        iBuf = new int [szBuf];
        cBuf = new char [szBuf];
    }

    catch (...) {
        throw OutOfMemory();
    }
}

The program output is:
Out of Memory!

14.3 Templates and Exceptions

Exceptions defined within a template class can be specific to each generated class. Given the following:

template <class T>
class Vector {
    public:
        class ExceptionRange { };    //exception class
};

A catch block would need to specify the type of vector that it is handling, as follows:

catch (Vector<int>:: ExceptionRange ) { // }

14.3.1 Using exception classes with templates

The following example demonstrates how to use exception classes with templates.

// Compile Options /GX
/**************************************************************
This program demonstrates how to use Exception classes with
templates. The template class vector is user defined.
Each vector type needs to have a handler defined or the exception 
will be handled by function terminate ().
**************************************************************/

#include <iostream>
template <class T>
class Vector {
        T* pT;
        int sz;
    public:
        Vector (int s=1);
        ~Vector();
        class ExceptionRange {};        //Exception class
        T& operator [] (int i);
        int size () const { return sz;}
};

template<class T>
inline Vector<T>::Vector(int s) { pT = new T[sz=s]; }

template<class T>
inline Vector<T>::~Vector() { delete [] pT; }

void range_error (Vector<int> & v) {
    v[v.size()+10];                    // trigger range error
}

template<class T>
T& Vector<T>::operator [] (int i) {
    if (0<=i && i <sz) return pT[i];
        throw ExceptionRange ();
    return pT[0];
}

void Do_Vector (Vector<int>& v) {
    range_error(v);
}

void main (void) {
    Vector <int> v(10);
    try {
        Do_Vector (v);
    }

    // Catch (Vector<class int>::ExceptionRange)
    catch (Vector <int>::ExceptionRange) {
        // Handler for vector range exception
        cout << "Exception: Vector out of range" << endl;
    }
}

Program Output:
Exception: Vector out of range

You would need to define handlers for each excepted type of vector; otherwise, the types not listed will be handled by terminate () function. The exception could be defined outside of the Vector class as a global to all vector types. Exceptions defined within a class follow the standard access rules. If a class only handles an exception internally, the exception class should be private or protected. If you want the exception to be handled externally, make it public or define it outside of the error-producing class.

14.3.2 Using exception classes with STL containers

The following is an example of how to use exception classes with STL containers.

// Compile Options /GX
/**************************************************************
This is a header file for the derived template class MyVector.
MyVector is a derived from Vector template class. It contains
the exception class ExceptionRange. The operator [] is overloaded
and throws the ExceptionRange when there is an attempt to
access an out of range vector object.
**************************************************************/

// MyVector.h
#ifndef MY_VECTOR_DEFINED
#define MY_VECTOR_DEFINED

#include <vector>
#include <iostream>

template<class T>
class MyVector : public vector<T,allocator<T> > {
    public:
        typedef allocator<T> A;
        explicit MyVector(const A& al = A()) : vector<T,A>(al) { };
        explicit MyVector(size_type n, const T& v = T(), const A& al = A())
                         : vector<T,A>(n, v, al) { } 
        MyVector(const MyVector& x) : vector<T,A>(x) {}
        MyVector(const_iterator first, const_iterator last, const A& al = A())
                         : vector<T,A>(first, last, al) {}

    class ExceptionRange {
        public:
            ExceptionRange (size_type _P) {
                cout << endl;
                cout <<"An attempt was made to access an element out of range" <<endl;
                cout << "Element for index: " << _P << " doesn't exist."  << endl;
            }
    };

    const_reference operator[](size_type _P) const throw (ExceptionRange) {
        if ( _P > ((end()-1) - begin())) {
            throw ExceptionRange(_P);
            return(*(end()));
        }
        return (*(begin() + _P));
    }

    reference operator[](size_type _P) throw (ExceptionRange) {
        if ( _P > ((end()-1) - begin())) {
            throw ExceptionRange(_P);
            return(*(end()));
        }
        return (*(begin() + _P));
    }
};
#endif

// VectorMain.cpp
/**************************************************************
    // Compile Options /GX
    /**************************************************************
    This program demonstrates how to use Exception classes with STL.
    MyVector is a derived from Vector template class.
    This is just one method to make the use of STL library more safe.
    **************************************************************/

    #include <iostream>
    #include "MyVector.h"

    void main() {
        MyVector<int> intVect;
        MyVector<int>::iterator intVectPtr;

        for(int i = 0; i < 20; i++)
            intVect.push_back(i*2);

        for(intVectPtr = intVect.begin(); intVectPtr != intVect.end(); intVectPtr++)
            cout << *intVectPtr << ", ";

        cout << endl << endl;

        try {
            for (int k = 0; k < 30 ;k++ )
                cout << intVect[k] <<", ";
        }

        catch (MyVector <int>::ExceptionRange) {
            cout << endl;
            cout << "Exception: Vector out of range" << endl;
        }
    }

Program Output:
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38,
An attempt was made to access an element out of range.
Element for index: 20 doesn't exist.
Exception: Vector out of range.

14.4 Exception Classes

The Standard C++ Library defines a base class exception as follows:

class exception {
    public:
        exception() throw();
        exception(const exception& rhs) throw();
        exception& operator=(const exception& rhs) throw();
        virtual ~exception() throw();
        virtual const char *what() const throw();
};

The class serves as the base class for all exceptions thrown by certain expressions and by the Standard C++ Library. The C-string value returned by what() is left unspecified by the default constructor, but may be defined by the constructors for certain derived classes. None of the member functions throws any exceptions.

14.4.1 Header <stdexcept> synopsis

#include <exception>
#include <string>
namespace std {
    class logic_error;
    class domain_error;
    class invalid_argument;
    class length_error;
    class out_of_range;
    class runtime_error;
    class range_error;
    class overflow_error;
    class underflow_error;
}

class logic_error
namespace std {
    class logic_error : public exception {
        public:
            logic_error(const string& what_arg);
    };
}

The class LOGIC_ERROR defines the type of objects thrown as exceptions to report errors that are, presumably, detectable before the program executes, such as violations of logical preconditions or class invariants.

logic_error(const string& what_arg);
Effects:
    Constructs an object of class logic_error.
Postcondition:
    what() == what_arg.data()

class domain_error
namespace std {
    class domain_error : public logic_error {
        public:
            domain_error(const string& what_arg);
    };
}

The class DOMAIN_ERROR defines the type of objects thrown as exceptions, by the implementation, to report domain errors.

domain_error(const string& what_arg);
Effects:
    Constructs an object of class domain_error.
Postcondition:
    what() == what_arg.data().

Class invalid_argument
namespace std {
    class invalid_argument : public logic_error {
        public:
            invalid_argument(const string& what_arg);
    };
}

The class INVALID_ARGUMENT defines the type of objects thrown as exceptions to report an invalid argument.

invalid_argument(const string& what_arg);
Effects:
    Constructs an object of class invalid_argument.
Postcondition:
    what() == what_arg.data().

class length_error
namespace std {
    class length_error : public logic_error {
        public:
            length_error(const string& what_arg);
    };
}

The class LENGTH_ERROR defines the type of objects thrown as exceptions to report an attempt to produce an object whose length exceeds its maximum allowable size.

length_error(const string& what_arg);
Effects:
    Constructs an object of class length_error.
Postcondition:
        what() == what_arg.data().

Class out_of_range
namespace std {
    class out_of_range : public logic_error {
        public:
            out_of_range(const string& what_arg);
    };
}

The class OUT_OF_RANGE defines the type of objects thrown as exceptions to report an argument value not in its expected range.

out_of_range(const string& what_arg);
Effects:
    Constructs an object of class out_of_range.
Postcondition:
    what() == what_arg.data().

class runtime_error
namespace std {
    class runtime_error : public exception {
        public:
            runtime_error(const string& what_arg);
    };
}

The class RUNTIME_ERROR defines the type of objects thrown as exceptions to report errors presumably detectable only when the program executes.

runtime_error(const string& what_arg);
Effects:
    Constructs an object of class runtime_error.
Postcondition:
    what() == what_arg.data().

class range_error
namespae std {
    class range_error : public runtime_error {
        public:
            range_error(const string& what_arg);
    };
}

The class RANGE_ERROR defines the type of objects thrown as exceptions to report range errors in internal computations.

range_error(const string& what_arg);
Effects:
    Constructs an object of class range_error.
Postcondition:
    what() == what_arg.data().

class overflow_error
namespace std {
    class overflow_error : public runtime_error {
        public:
            overflow_error(const string& what_arg);
    };
}

The class OVERFLOW_ERROR defines the type of objects thrown as exceptions to report an arithmetic overflow error.

overflow_error(const string& what_arg);
Effects:
    Constructs an object of class overflow_error.
Postcondition:
    what() == what_arg.data().

class underflow_error
namespace std {
    class underflow_error : public runtime_error {
        public:
            underflow_error(const string& what_arg);
    };
}

The class UNDERFLOW_ERROR defines the type of objects thrown as exceptions to report an arithmetic underflow error.

underflow_error(const string& what_arg);
Effects:
    Constructs an object of class underflow_error.
Postcondition:
    what() == what_arg.data().

14.4.2 Assertions

This provides macros for documenting C++ program assertions, and for disabling the assertion checks.
Header <cassert>
Type Name(s)
Macro assert
The contents are the same as the Standard C library.

14.4.3 Error numbers

Header <cerrno> :
Type Name(s)   
Macros EDOM ERANGE  errno
The contents are the same as the Standard C library.

14.4.4 Floating-point exception class

sample

The following example demonstrates how to implement a floating-point exception class.

// Floating-point exception class sample
// Compile Options /GX
/*******************************************************************
This program demonstrates how to use the exception classes
from the diagnostics library to handle floating point exceptions;
float_error class is derived from logic_error base class.
********************************************************************/

#include <exception>
#include <iostream>
#include <complex>

//floating-point exception class
class float_error : public logic_error {
    public:
        float_error (char buffer[]) : logic_error (buffer) {
            cout << "Floating point math error occurred in your program ";
            cout <<"More details below:"<<endl;
        }
};
//Math error handler
int _matherr (struct _exception * ex) {
    char buffer [128];
    const char * ErrorType;
    // Determine type of error.
    switch (ex->type) {
        case _DOMAIN:
            ErrorType = "Domain Error: ";
            break;
        case _SING:
            ErrorType = "Singularity Error:";
            break;
        case _OVERFLOW:
            ErrorType = "Overflow Error:";
            break;
        case _UNDERFLOW:
            ErrorType = "Underflow Error:";
            break;
        case _PLOSS:
            ErrorType = "Partial Loss of significance:";
            break;
        case _TLOSS:
            ErrorType = "Total loss of significance:";
            break;
        default:
            ErrorType = "Some other math error:";
    }

    // Construct error string.
    sprintf (buffer, "%s: %s(%g,%g) returns %g", ErrorType,
             ex->name,ex->arg1,ex->arg2,ex->retval);
    // Throw an exception.
    throw float_error(buffer);
    return 0;
}

void  TestMathErrors(double (*fp) (double), double arg)   {
    // Next text
    bool caught = false;
    // Generate a floating-point error.
    try {
        double x = fp (arg);
    }
    catch (exception & ex) {
        cout << ex.what() << endl << endl;
        caught = true;
    }

    if (!caught)
        cout << "didn't catch exception through materr\r\n";
}

int main (void) {
  typedef double (*fp) (double);
  //Array to the several functions
  fp math_function [] = {&sqrt, &log, &sin, &tan, &acos};
  double arg []= {-1.0, 0.0, 1.5e25, 1.5e25, 2};

    for (int n=0; n<5; n++) 
        TestMathErrors(math_function[n],arg[n]);

    return 0;
}

Program output is:

Floating point math error occurred in your program. More details below:
Domain Error:: sqrt(-1,-3.10504e+231) returns -1.#IND
Floating point math error occurred in your program. More details below:
Singularity Error:: log(0,-3.10504e+231) returns -1.#INF
Floating point math error occurred in your program. More details below:
Total loss of significance:: sin(1.5e+025,-3.10504e+231) returns -1.#IND
Floating point math error occurred in your program. More details below:
Total loss of significance:: tan(1.5e+025,-3.10504e+231) returns -1.#IND
Floating point math error occurred in your program. More details below:
Domain Error: : acos(2,-3.10504e+231) returns -1.#IND