|< C & C++ Exception Handling 1 | Main | C & C++ Typecasting 1 >| Site Index | Download |


 

 

 

 

 

MODULE 21a

C AND C++ EXCEPTION HANDLING 2

 In the worst case, there must be an emergency exit!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

My Training Period: xx hours

 

21.8  The Exception Specifications

  • Exception specifications are used to provide summary information about what exceptions can be thrown out of a function. Exceptions not listed in an exception specification should not be thrown from that function.

  • An exception specification consists of the keyword throw after the function's parameter list, followed by a list of potential exceptions, for example:

void test(int somecode) throw (bad_code, no_auth);
  • An exception specification isn't considered a part of a function's type. Therefore, it doesn't affect overload resolution. That means pointers to functions and pointers to member functions may contain an exception specification, for example:

void (*PtrFunct)(double) throw(string, double); 
  • PtrFunct is a pointer to a function that may throw string or double. You can assign to a function whose exception specification is as restrictive as, or more restrictive than PtrFunct 's exception specification.

  • An exception specification P is said to be more restrictive than an exception specification Q if the set of exceptions P contains is a subset of Q's exceptions. In other words, P contains every exception in Q but not vice versa.  For example:

void (*PtrFunct)(double) throw(string, double);

// more restrictive than PtrFunct:
void One(double) throw (string); 
// as restrictive as PtrFunct:
void Two(double) throw (string, double); 
// less restrictive than PtrFunct:
void Three(double) throw (string, double, bool); 
PtrFunct = One; 	// OK
PtrFunct = Two; 	// OK

PtrFunct = Three;  // error, Three is not subset of the PtrFunct

  • A function with no exception-specification allows all exceptions. A function with an empty exception specification doesn't allow any exceptions, for example:

class Test
{
    public: 
    	// may throw any exception
    	int One(char *VarPtr);
    	// doesn't throw any exception
    	int Two(double *VarPtr1) throw();
};

Exception specification

Meaning

throw()

The function does not throw any exception.

throw(...)

The function can throw an exception.

throw(type)

The function can throw an exception of type type.

 

Table 21.2: Exception specification

Example

Description

void Funct() throw(int)

The function may throw an int exception.

void Funct() throw()

The function will throw no exceptions.

void Funct() throw(char*, T)

The function may throw a char* and/or a T, user defined type exception.

void Funct() or void Funct(...)

The function may throw anything.

 

Table 21.3: Exception specification example

// exception specification

#include <iostream>

using namespace std;

 

// handler function

void handler()

{cout<<"In the handler()\n";}

 

// int throw...

void Funct1(void) throw(int)

{

       static int x = 1;

       cout<<"Funct1() call #"<<x++<<endl;

       cout<<"About to throw 1\n";

       if (1)

       throw 1;

}

// empty throw...

void Funct5(void) throw()

{

       try

       {Funct1();}

       catch(...)

       {handler();}

}

 

// invalid, doesn't handle the int exception thrown from Funct1()

// void Funct3(void) throw()  

// {

//   Funct1();

// }

 

void Funct2(void)

{

       try

           {Funct1();}

       catch(int)

           {handler();}

}

 

// assume extern "C" functions don't throw exceptions

extern "C" void Funct4(void);

void Funct4(void)

    {Funct1();}

 

int main()

{

       Funct2();

       try

           {Funct4();}

       catch(...)

           {cout<<"Caught exception from Funct4()\n";}

       Funct5();

       return 0;

}

 

Output:

 

C++ exception handling specification

 

21.9  Exception Handling Overhead

21.10  Standard Exceptions

Class hierarchy

Description

exception

 

    bad_alloc

Thrown by new, an allocation request fails.

    bad_cast

Thrown by dynamic_cast when failed cast to a reference type.

    bad_exception

Thrown when an exception doesn't match any catch clause.

    bad_typeid

Thrown by typeid operator when the operand for typeid is a NULL pointer.

The logical errors are normally caused by programmer mistakes.

    logic_error

As the base class for all exceptions thrown to report errors presumably detectable before the program executes, such as violations of logical preconditions.

        domain_error

As the base class for all exceptions thrown to report a domain error.

        invalid_argument

As the base class for all exceptions thrown to report an invalid argument.

        length_error

As the base class for all exceptions thrown to report an attempt to generate an object too long to be specified.

        out_of_range

As the base class for all exceptions thrown to report an argument that is out of its valid range.

The run-time errors normally occur because of mistakes in either the library functions or in the run-time system

    runtime_error

As the base class for all exceptions thrown to report errors presumably detectable only when the program executes.

        overflow_error

As the base class for all exceptions thrown to report an arithmetic overflow.

        range_error

As the base class for all exceptions thrown to report a range error.

        underflow_error

As the base class for all exceptions thrown to report an arithmetic underflow.

    ios_base::failure

The member class serves as the base class for all exceptions thrown by the member function clear() in template class basic_ios.

 

Table 21.4:  exception class

class exception

{

       public:

       exception( ) throw( );

       exception(const exception& right) throw( );

       exception& operator=(const exception& right) throw( );

       virtual ~exception( ) throw( );

       virtual const char *what( ) const throw( );

};

  • Some functions of the standard C++ library send exceptions that can be caught by including them within a try block. These exceptions are sent with a class derived from std::exception as their type.  It is better to use these exceptions instead of creating your own, because these exceptions have been tested.

  • Because this is a class hierarchy, if you include a catch block to capture any of the exceptions of this hierarchy using the argument by reference that is by adding an ampersand, & after the type, you will also capture all the derived ones.

  • Referring to the hierarchy of the exception, exceptions are caught in a bottom-down hierarchy: Specific derived classes exceptions are handled first, followed by less specific groups of exceptions that is, up to the base classes and, finally, a catch(...) handler:

  • Handlers of the specific derived objects must appear before the handlers of base classes. This is because handlers are tried in order of appearance. It's therefore possible to write handlers that are never executed; for example, by placing a handler for a derived class after a handler for a corresponding base class.

  • You can use the classes of standard hierarchy of exceptions to throw your exceptions or derive new classes from them.

  • The following example catches an exception of type bad_typeid (derived from exception) that is generated when requesting information about the type pointed by a NULL pointer:

 

// standard exceptions program example

#include <iostream>

#include <exception>

#include <typeinfo>

using namespace std;

 

class Test1

{    virtual Funct() { };    };

 

int main ()

{

     try {

                Test1 * var = NULL;

                typeid (*var);

            }

catch (std::exception& typevar)

{    cout<<"Exception: "<<typevar.what()<<endl;    }

     return 0;

}

 

Output:

 

C++ standard exception handling

class out_of_range : public logic_error 
{
    public:
     out_of_range(const string& message);
};

// out_of_range example

#include <string>

#include <iostream>

using namespace std;

 

int main( )

{

       try

       {

              string strg1("Test");

              string strg2("ing");

              strg1.append(strg2, 4, 2);

              cout<<strg1<<endl;

       }

       catch (exception &e)

       {

              cerr<<"Caught: "<<e.what()<<endl;

              cerr<<"Type: "<<typeid(e).name()<<endl;

       };

       return 0;

}

 

Output

 

C++ standard exception out_of_range program example

typedef

Description

terminate_handler

A type that describes a pointer to a function suitable for use as a terminate_handler.

unexpected_handler

A type that describes a pointer to a function suitable for use as an unexpected_handler.

 

Table 21.5:  <exception> typedef

 

 

Member function

Description

set_terminate()

Establishes a new terminate_handler to be called at the termination of the program.

set_unexpected()

Establishes a new unexpected_handler to be when an unexpected exception is encountered.

terminate()

Calls a terminate handler.

uncaught_exception()

Returns true only if a thrown exception is being currently processed.

unexpected()

Calls an unexpected handler.

 

Table 21.6:  <exception> member function

 

 

Class

Description

bad_exception

The class describes an exception that can be thrown from an unexpected_handler.

exception

The class serves as the base class for all exceptions thrown by certain expressions and by the Standard C++ Library.

 

Table 21.7:  <exception> class member

// bad_cast

// need to enable the Run-Time Type Info,

// rtti of your compiler.  You will learn

// typecasting in another Module…

#include <typeinfo>

#include <iostream>

using namespace std;

 

class Myshape

{

     public:

     virtual void myvirtualfunc() const {}

};

 

class mytriangle: public Myshape

{

     public:

     virtual void myvirtualfunc() const

     {   };

};

 

int main()

{

   Myshape Myshape_instance;

   Myshape &ref_Myshape = Myshape_instance;

  

   try {

            // try the run time typecasting, dynamic_cast

            mytriangle &ref_mytriangle = dynamic_cast<mytriangle&>(ref_Myshape);

   }

   catch (bad_cast) {

       cout<<"Can't do the dynamic_cast lor!!!"<<endl;

      cout<<"Caught: bad_cast exception. Myshape is not mytriangle.\n";

   }

  return 0;

}

 

Output:

 

C++ standard exception bad_cast and RTTI example

 

// bad_alloc, first version, when the allocation is OK

#include <new>

#include <iostream>

using namespace std;

 

int main()

{

   char* ptr;

  

   unsigned long int Test = sizeof(size_t(0)/3);

   cout<<"The size of variable Test = "<<Test<<endl;

   try

   {

       // try some allocation...

       // size of an array must not exceed certain bytes

       ptr = new char[size_t(0)/3]

       delete[ ] ptr;

   }

   catch(bad_alloc &thebadallocation)

   {

      cout<<thebadallocation.what()<<endl;

   };

  return 0;

 }

 

Output:

 

C++ standard exception bad_alloc when the allocation is OK

sizeof(size_t(0)/3)

sizeof(~size_t(0)/3)

C++ standard exception bad_alloc when the allocation is not OK

 

// set_unexpected

#include <exception>

#include <iostream>

using namespace std;

 

void myfunction()

{

   cout<<"Testing myfunction()."<<endl;

   //terminate() handler

   terminate();

}

 

int main( )

{

   unexpected_handler oldHandler = set_unexpected(myfunction);

   // unexpected() function call

   unexpected();

   return 0;

}

 

Output:

C++ standard Windows exception debug error dialog box - Abort, Retry or Ignore

 

 

C++ Windows standard exception - Abort, Retry and Ignore

 

// bad_typeid

#include <typeinfo>

#include <iostream>

using namespace std;

 

class Test

{

    public:

    // object for a class needs vtable for the rtti...

    Test();

    virtual ~Test();

 };

 

int main()

{

     Test *ptrvar = NULL;

    

    try {

              // the error condition

             cout<<typeid(*ptrvar).name()<<endl; 

           }

    catch (bad_typeid){

       cout<<"The object is NULL"<<endl;

       }

    return 0;

}

 

Output:

 

C++ standard exception bad_typeid exception class

 

// domain_error and typeid()

#include <iostream>

using namespace std;

 

int main()

{

   try

   {

      throw domain_error("Some error with your domain!");

   }

   catch (exception &err)

   {

      cerr<<"Caught: "<<err.what()<<endl;

      cerr<<"Type: "<<typeid(err).name()<<endl;

   };

    return 0;

}

 

Output:

 

C++ standard exception domain_error and typeid() example

 

// invalid_argument

#include <bitset>

#include <iostream>

using namespace std;

 

int main()

{

   try

   {

// binary wrongly represented by char X, a template based…

bitset<32> bitset(string("0101001X01010110000"));

   }

   catch (exception &err)

   {

      cerr<<"Caught "<<err.what()<<endl;

      cerr<<"Type "<<typeid(err).name()<<endl;

   };

    return 0;

}

 

Output:

 

C++ standard exception invalid_argument example

 

// runtime_error

#include <iostream>

using namespace std;

 

int main()

{

   // runtime_error

   try

   {

      locale testlocale("Something");

   }

   catch(exception &err)

   {

      cerr<<"Caught "<<err.what()<<endl;

      cerr<<"Type "<<typeid(err).name()<<endl;

   };

    return 0;

}

 

Output:

 

C++ standard exception runtime_error program example

 

// overflow_error, storage reserved is not enough

#include <bitset>

#include <iostream>

using namespace std;

 

int main()

{

   try

   {

      // a template based…

      bitset<100> bitset;

      bitset[99] = 1;

      bitset[0] = 1;

         // to_ulong(), converts a bitset object to the integer

         // that would generate the sequence of bits

      unsigned long Test = bitset.to_ulong();

   }

   catch(exception &err)

   {

      cerr<<"Caught "<<err.what()<<endl;

      cerr<<"Type "<<typeid(err).name()<<endl;

   };

    return 0;

}

 

Output:

 

C++ standard exception overflow_error program example

 

// range_error

#include <iostream>

using namespace std;

 

int main()

{

   try

   {

      throw range_error("Some error in the range!");

   }

   catch(exception &Test)

   {

      cerr<<"Caught: "<<Test.what()<<endl;

      cerr<<"Type: "<<typeid(Test).name()<<endl;

   };

   return 0;

}

 

Output:

 

C++ standard exception range_error program example

 

// underflow_error, the negative storage...

#include <iostream>

using namespace std;

 

int main()

{

   try

   {

      throw underflow_error("The negative storage?");

   }

   catch(exception &Test)

   {

      cerr<<"Caught: "<<Test.what()<<endl;

      cerr<<"Type: "<<typeid(Test).name()<<endl;

   };

 return 0;

}

 

Output:

 

C++ standard exception underflow_error program example

// ***********-except.cpp-***********

// exception, class and destructor

#include <iostream>

using namespace std;

 

// a prototype...

void TestFunct(void);

 

// class Test1 declaration...

class Test1

{

        public:

            Test1(){ };

            ~Test1(){ };

            const char *TestShow() const

            {

                cout<<"In class member function *TestShow():\n";

                return "  Exception in Test1 class.";

            }

};

 

// another class declaration, DestrTest...

class DestrTest

{

        public:

            DestrTest();

            ~DestrTest();

};

 

// constructor class implementation

DestrTest::DestrTest()

{

        cout<<"Next, in constructor DestrTest():\n";

        cout<<"  Constructing the DestrTest...\n";

}

 

// destructor class implementation

DestrTest::~DestrTest()

{

        cout<<"Next, in destructor ~DestrTest():\n";

        cout<<"  Destructing the DestrTest...\n";

}

 

void TestFunct()

{

        // instantiate an object, constructor invoked...

        DestrTest p;

        cout<<"Next in TestFunct(): \n  Throwing Test1 type exception...\n";

        // first throw...

        throw Test1();

}

 

int main()

{

        cout<<"Starting in main()...\n";

        try

        {

                cout<<"Now, in the try block: \n  Calling TestFunct()...\n";

                TestFunct();

        }

        // instantiate another object, constructor invoked...

        catch(Test1 q)

        {

                cout<<"Next, in catch handler:\n";

                cout<<"  Caught Test1 type exception...\n";

                cout<<q.TestShow()<<"\n";

        }

        catch(char *strg)

        {

                cout<<"Caught char pointer type exception: "<<strg<<"\n";

        }

        cout<<"Back in main...\n";

        return 0;

}

 

[bodo@bakawali ~]$ g++ except.cpp -o except

[bodo@bakawali ~]$ ./except

 

Starting in main()...

Now, in the try block:

  Calling TestFunct()...

Next, in constructor DestrTest():

  Constructing the DestrTest...

Next in TestFunct():

  Throwing Test1 type exception...

Next, in destructor ~DestrTest():

  Destructing the DestrTest...

Next, in catch handler:

  Caught Test1 type exception...

In class member function *TestShow():

  Exception in Test1 class.

Back in main...

 

tenouk fundamental of C++ object oriented tutorial

 

 

 

 

 

 

 

 

 

 

 

Further C/C++ exception handling related reading:

 

  1. The source code for this tutorial is available in C/C++ Exception Handling source code.

  2. Check the best selling C / C++, Object Oriented and pattern analysis books at Amazon.com.

 

 

 

 

 

 

 

|< C & C++ Exception Handling 1 | Main | C & C++ Typecasting 1 >| Site Index | Download |


C & C++ Exception Handling:  Part 1 | Part 2