Chapter 8: Exceptions

We're always interested in getting feedback. E-mail us if you like this guide, if you think that important material is omitted, if you encounter errors in the code examples or in the documentation, if you find any typos, or generally just if you feel like e-mailing. Send your email to Frank Brokken.

Please state the document version you're referring to, as found in the title (in this document: 5.2.0a) and please state the paragraph you're referring to.

All mail received is seriously considered, and new (sub)releases of the Annotations will normally reflect your suggestions for improvements. Except for the incidental case I will not otherwise acknowledge the receipt of suggestions for improvements. Please don't misinterpret this for lack of appreciation.

In C there are several ways to have a program react to situations which break the normal unhampered flow of the program:

In C++ all the above ways to handle flow-breaking situations are still available. However, the last way, using setjmp() and longjmp() isn't often seen in C++ (or even in C) programs, due to the fact that the program flow is completely disrupted.

In C++ the alternative to using setjmp() and longjmp() are exceptions. Exceptions implement a mechanism by which a controlled non-local exit is realized within the context of a C++ program, without the disadvantages of longjmp() and setjmp().

Exceptions are the proper way to bail out of a situation which cannot be handled easily by a function itself, but which are not disastrous enough for the program to terminate completely. Also, exceptions provide a flexible layer of flow control between the short-range return and the crude exit().

In this chapter the use of exceptions and their syntax will be discussed. First an example of the different impacts exceptions and setjmp() and longjmp() have on the program will be given. Then the discussion will dig into the formalities of using exceptions.

8.1: Using exceptions: syntax elements

With exceptions the following syntactical elements are encountered:

8.2: An example using exceptions

In the next two sections the same basic program will be used. The program uses two classes, Outer and Inner. An Outer object is created in the main() function, and the function Outer::fun() is called. Then, in the Outer::fun() member an Inner object is constructed. After constructing the Inner object, its member function fun() is called.

That's about it. The function Outer::fun() terminates, and the destructor of the Inner object is called. Then the program terminates and the destructor of the Outer object is called. Here is the basic program:

    #include <iostream>
    using namespace std;
    
    class Inner
    {
        public:
            Inner();
            ~Inner();
            void fun();    
    };
    
    class Outer
    {
        public:
            Outer();
            ~Outer();
            void fun();
    };
    
    Inner::Inner()
    {
        cout << "Inner constructor\n";
    }    
    
    Inner::~Inner()
    {
        cout << "Inner destructor\n";
    }    
    
    void Inner::fun()
    {
        cout << "Inner fun\n";
    }    
    
    Outer::Outer()
    {
        cout << "Outer constructor\n";
    }    
    
    Outer::~Outer()
    {
        cout << "Outer destructor\n";
    }    
    
    void Outer::fun()
    {
        Inner in;
    
        cout << "Outer fun\n";
        in.fun();
    }    
    
    int main()
    {
        Outer out;
    
        out.fun();
    }

    /*
        Generated output:
    Outer constructor
    Inner constructor
    Outer fun
    Inner fun
    Inner destructor
    Outer destructor
    */
After compiling and running, the program's output is completely as expected, and it is exactly what we want: the destructors are called in their correct order, reversing the calling sequence of the constructors.

Now let's focus our attention on two variants, in which we simulate a non-fatal disastrous event to take place in the Inner::fun() function, which is supposedly handled somewhere at the end of the function main(). We'll consider two variants. The first variant will try to handle this situation using setjmp() and longjmp(); the second variant will try to handle this situation using C++'s exception mechanism.

8.2.1: No exceptions: `setjmp()' and `longjmp()'

In order to use setjmp() and longjmp() the basic program from section 8.2 is slightly modified to contain a variable jmp_buf jmpBuf. The function Inner::fun() now calls longjmp, simulating a disastrous event, to be handled at the end of the function main(). In main() we see the standard code defining the target location of the long jump, using the function setjmp(). A zero return value indicates the initialization of the jmp_buf variable, upon which the Outer::fun() function is called. This situation represents the `normal flow'.

To complete the simulation, the return value of the program is zero only if the program is able to return from the function Outer::fun() normally. However, as we know, this won't happen: Inner:fun() calls longjmp(), returning to the setjmp() function, which (at this time) will not return a zero return value. Hence, after calling Inner::fun() from Outer::fun() the program proceeds beyond the if-statement in the main() function, and the program terminates with the return value 1. Now try to follow these steps by studying the following program source, modified after the basic program given in section 8.2:

    #include <iostream>
    #include <setjmp.h>
    #include <cstdlib>

    using namespace std;
    
    class Inner
    {
        public:
            Inner();
            ~Inner();
            void fun();
    };
    
    class Outer
    {
        public:
            Outer();
            ~Outer();
            void fun();
    };
    
    jmp_buf
        jmpBuf;
    
    Inner::Inner()
    {
        cout << "Inner constructor\n";
    }    
    
    void Inner::fun()
    {
        cout << "Inner fun()\n";
        longjmp(jmpBuf, 0);
    }    
    
    Inner::~Inner()
    {
        cout << "Inner destructor\n";
    }    
    
    Outer::Outer()
    {
        cout << "Outer constructor\n";
    }    
    
    Outer::~Outer()
    {
        cout << "Outer destructor\n";
    }    
    
    void Outer::fun()
    {
        Inner in;

        cout << "Outer fun\n";
        in.fun();
    }    
    
    int main()
    {
        Outer out;
    
        if (!setjmp(jmpBuf))
        {
            out.fun();
            return 0;
        }
        return 1;
    }
    /*
        Generated output:
    Outer constructor
    Inner constructor
    Outer fun
    Inner fun()
    Outer destructor
    */
From the program generated by this program it is clear that the destructor of the class Inner is not executed. This is a direct result of the non-local characteristic of the call to longjmp(): processing proceeds immediately from the longjmp() call in the member function Inner::fun() to the function setjmp() in main(). There, its return value is zero, so the program terminates with return value 1. What is important here is taht the call to the destructor Inner::~Inner(), waiting to be executed at the end of Outer::fun(), is never reached.

Since this example shows that the destructors of objects can easily be skipped when longjmp() and setjmp() are used, it's probably best to avoid these function completely in C++ programs.

8.2.2: Exceptions: the preferred alternative

In C++ exceptions are the best alternative to setjmp() and longjmp(). In this section an example using exceptions is presented. Again, the program is derived from the basic program, given in section 8.2:
    #include <iostream>
    using namespace std;
    
    class Inner
    {
        public:
            Inner();
            ~Inner();
            void fun();
    };
    
    class Outer
    {
        public:
            Outer();
            ~Outer();
            void fun();
    };
    
    Inner::Inner()
    {
        cout << "Inner constructor\n";
    }    
    
    Inner::~Inner()
    {
        cout << "Inner destructor\n";
    }    
    
    void Inner::fun()
    {
        cout << "Inner fun\n";
        throw 1;
        cout << "This statement is not executed\n";
    }    
    
    Outer::Outer()
    {
        cout << "Outer constructor\n";
    }    
    
    Outer::~Outer()
    {
        cout << "Outer destructor\n";
    }    
    
    void Outer::fun()
    {
        Inner in;

        cout << "Outer fun\n";
        in.fun();
    }    
    
    
    int main()
    {
        Outer out;

        try
        {
            out.fun();
        }
        catch (...)
        {}
    }
    /*
        Generated output:
    Outer constructor
    Inner constructor
    Outer fun
    Inner fun
    Inner destructor
    Outer destructor
    */
In this program an exception is thrown, where a longjmp() was used in the program in section 8.2.1. The comparable construct for the setjmp() call in that program is represented here by the try and catch blocks. The try block surrounds statements (including function calls) in which exceptions are thrown, the catch block may contain statements to be executed just after throwing an exception.

So, comparably to the example given in section 8.2.1, the execution of function Inner::fun() terminates, albeit with an exception, rather than a longjmp(). The exception is caught in main(), and the program terminates. When the output from the current program is inspected, we notice that the destructor of the Inner object, created in Outer::fun() is now correctly called. Also notice that the execution of the function Inner::fun() really terminates at the throw statement: the insertion of the text into cout, just beyond the throw statement, doesn't take place.

Hopefully we now have raised your appetite for exceptions by showing that

8.3: Throwing exceptions

Exceptions may be generated in a throw statement. The throw keyword is followed by an expression, which results in a value of a certain type. For example:
    throw "Hello world";        // throws a char *
    throw 18;                   // throws an int
    throw string("hello");      // throws a string
Objects defined locally in functions are automatically destroyed once exceptions are thrown within these functions. However, if the object itself is thrown, the exception catcher receives a copy of the thrown object. This copy is constructed just before the local object is destroyed.

The next example illustrates this point. Within the function Object::fun() a local Object toThrow is created, which is thereupon thrown as an exception. The exception is caught outside of Object::fun(), in main(). At this point the thrown object doesn't actually exist anymore, Let's first take a look at the sourcetext:

    #include <iostream>
    using namespace std;
    
    class Object
    {
        string
            d_name;

        public:
            Object(string name)
            :
                d_name(name)
            {
                cout << "Object constructor of " << d_name << "\n";
            }    
            Object(Object const &other)
            :
                d_name(other.d_name + " (copy)")
            {
                cout << "Copy constructor for " << d_name << "\n";
            }    
            ~Object()
            {
                cout << "Object destructor of " << d_name << "\n";
            }    
            void fun()
            {
                Object toThrow("'local object'");
            
                cout << "Object fun() of " << d_name << "\n";
                throw toThrow;
            }    
            void hello()
            {
                cout << "Hello by " << d_name << "\n";
            }        
    };
    
    int main()
    {
        Object out("'main object'");
    
        try
        {
            out.fun();
        }
        catch (Object o)
        {
            cout << "Caught exception\n";
            o.hello();
        }
    }
    /*
        Generated output:
Object constructor of 'main object'
Object constructor of 'local object'
Object fun() of 'main object'
Copy constructor for 'local object' (copy)
Object destructor of 'local object'
Copy constructor for 'local object' (copy) (copy)
Caught exception
Hello by 'local object' (copy) (copy)
Object destructor of 'local object' (copy) (copy)
Object destructor of 'local object' (copy)
Object destructor of 'main object'
    */
The class Object defines some simple constructors and members. The copy constructor is special in that it adds the text " (copy)" to the received name, to allow us to monitor the construction and destruction of objects more closely. The member function Object::fun() generates the exception, and throws its locally defined object. Just before the exception the following output is generated by the program:
    Object constructor of 'main object'
    Object constructor of 'local object'
    Object fun() of 'main object'
Now the exception is generated, resulting in the next line of output:
    Copy constructor for 'local object' (copy)
The throw clause receives the local object, and treats it as a value argument: it creates a copy of the local object. Following this, the exception is processed: the local object is destroyed, and the catcher catches an Object, again a value parameter. Hence, another copy is created. Threfore, we see the following lines:
    Object destructor of 'local object'
    Copy constructor for 'local object' (copy) (copy)
Now we are inside the catcher, who displays its message:
    Caught exception
followed by the calling of the hello() member of the received object. This member also shows us that we received a copy of the copy of the local object of the Object::fun() member function:
    Hello by 'local object' (copy) (copy)
Finally the program terminates, and its still living objects are now destroyed in their reversed order of creation:
    Object destructor of 'local object' (copy) (copy)
    Object destructor of 'local object' (copy)
    Object destructor of 'main object'

If the catcher would have been implemented so as to receive a reference to an object (which you could do by using `catch (Object &o)'), then the double copy would have been avoided. In that case the output of the program would have been:

Object constructor of 'main object'
Object constructor of 'local object'
Object fun() of 'main object'
Copy constructor for 'local object' (copy)
Object destructor of 'local object'
Caught exception
Hello by 'local object' (copy)
Object destructor of 'local object' (copy)
Object destructor of 'main object'
This shows us that only a single copy of the local object has been used.

Of course, it's a bad idea to throw a pointer to a locally defined object: the pointer is thrown, but the object to which the pointer refers dies once the exception is thrown, and the catcher receives a wild pointer. Bad news....

Summarizing, local objects are thrown as copied objects, pointers to local objects should not be thrown. However, it is possible to throw pointers or references to dynamically generated objects. In this case one must take care that the generated object is properly deleted when the generated exception is caught, to prevent a memory leak.

Exceptions are thrown in situations where a function can't continue its normal task anymore, although the program is still able to continue. Imagine a program which is an interactive calculator. The program continuously requests expressions, which are then evaluated. In this case the parsing of the expression may show syntactical errors; and the evaluation of the expression may result in expressions which can't be evaluated, e.g., because of the expression resulting in a division by zero. Also, the calculator might allow the use of variables, and the user might refer to non-existing variables: plenty of reasons for exceptions to be thrown, but no overwhelming reason to terminate the program. In the program, the following code may be used, all throwing exceptions:

    if (!parse(expressionBuffer))           // parsing failed 
        throw "Syntax error in expression";

    if (!lookup(variableName))              // variable not found
        throw "Variable not defined";

    if (divisionByZero())                   // unable to do division 
        throw "Division by zero is not defined";
The location of these throw statements is immaterial: they may be placed deeply nested within the program, or at a more superficial level. Furthermore, functions may be used to generate the expression which is then thrown. A function
char const *formatMessage(char const *fmt, ...);

would allow us to throw more specific messages, like

    if (!lookup(variableName))
        throw formatMessage("Variable '%s' not defined", variableName);

8.3.1: The empty `throw' statement

Situations may arise in which it is required to inspect a thrown exception. Depending on the nature of the received exception, the program may continue its normal operation, or a serious event took place, requiring a more drastic reaction by the program. In a server-client situation the client may enter requests to the server in a queue. Every request placed in the queue is normally answered by the server, telling the client that the request was successfully completed, or that some sort of error has occurred. Actually, the server may have died, and the client should be able to discover this calamity, by not waiting indefinitely for the server to reply.

In this situation an intermediate exception handler is called for. A thrown exception is first inspected at the middle level. If possible it's processed there. If it is not possible to process the exception at the middle level, it is passed on, unaltered, to a more superficial level, where the really tough exceptions are handled.

By placing an empty throw statement in the code handling an exception the received exception is passed on to the next level that might be able to process that particular type of exception.

In our server-client situation a function

initialExceptionHandler(char *exception)

could be designed to do so. The received message is inspected. If it's a simple message it's processed, otherwise the exception is passed on to an outer level. The implementation of initialExceptionHandler() shows the empty throw statement:

    void initialExceptionHandler(char *exception)
    {
        if (!plainMessage(exception))
            throw;

        handleTheMessage(exception);
    }
As we will see below (section 8.5), the empty throw statement passes on the exception received in a catch-block. Therefore, a function like initialExceptionHandler() can be used for a variety of thrown exceptions, as long as the argument used with initialExceptionHandler() is compatible with the nature of the received exception.

Does this sound intriguing? Then try to follow the next example, which jumps slightly ahead to the topics covered in chapter 14. The next example may be skipped, though, without loss of continuity.

We can now state that a basic exception handling class can be constructed from which specific exceptions are derived. Suppose we have a class Exception, containing a member function ExceptionType Exception::severity(). This member function tells us (little wonder!) the severity of a thrown exception. It might be Message, Warning, Mistake, Error or Fatal. Furthermore, depending on the severity, a thrown exception may contain less or more information, somehow processed by a function process(). In addition to this, all exceptions have a plain-text producing member function, e.g., toString(), telling us a bit more about the nature of the generated exception. Using polymorphism, process() can be made to behave differently, depending on the nature of a thrown exception, when called through a basic Exception pointer or reference.

In this case, a program may throw all these five types of exceptions. Let's assume that the Message and Warning exceptions are processable by our initialExceptionHandler(). Then its code would become:

    void initialExceptionHandler(Exception const *e)
    {
        cout << e->toString() << endl;  // show the plain-text information

        if 
        (
            e->severity() != ExceptionWarning 
            &&
            e->severity() != ExceptionMessage
        )
            throw;                      // Pass on other types of Exceptions

        e->process();               // Process a message or a warning
        delete e;
    }
Due to polymorphism (see chapter 14), e->process() will either process a Message or a Warning. Thrown exceptions are generated as follows:
    throw new Message(<arguments>);
    throw new Warning(<arguments>);
    throw new Mistake(<arguments>);
    throw new Error(<arguments>);
    throw new Fatal(<arguments>); 
All of these exceptions are processable by our initialExceptionHandler(), which may decide to pass exceptions upward for further processing or to process exceptions itself. The polymorphic exception class is developed further in section 14.7.

8.4: The try block

The try-block surrounds statements in which exceptions may be thrown. As we have seen, the actual throw statement can be placed everywhere, not necessarily within the try-block. It may, for example, be placed in a function which is called from within the try-block, either directly or indirectly.

The keyword try is followed by a set of curly braces, which acts like a standard C++ compound statement: multiple statements and variable definitions may be placed here.

It is possible (and very common) to create levels in which exceptions may be thrown. For example, code within the main() function is surrounded by a try-block, forming an outer level in which exceptions can be handled. Within the try-block in main(), functions are called which may also contain try-blocks, forming the next level in which exceptions may be placed. As we have seen (in section 8.3.1), exceptions thrown in inner level try-blocks may or may not be processed at that level. By placing an empty throw in an exception handler, the thrown exception is passed on to the next (outer) level.

If an exception is thrown outside of any try-block, then the default way to handle (uncaught) exceptions is used, which is normally to abort the program. Try to compile and run the following tiny program, and see what happens:

    int main()
    {
        throw "hello";
    }

8.5: Catching exceptions

The catch block contains code that is executed when an exception is thrown. Since expressions are thrown, the catch-block must know what kind of exceptions it should be able to handle. Therefore, the keyword catch is followed by a parameter list having one parameter, which is the type of the exception-expression that is handled by the catch block. So, an exception handler for char const * exceptions will have the following form:
    catch (char const *message)
    {
        // code to handle the message
    }
Earlier (section 8.3) we've seen that such a message doesn't have to be thrown as a static string. It's also possible for a function to return a string, which is then thrown as an exception. However, if such a function creates the string that is thrown as an exception dynamically, the exception handler will normally have to delete the allocated memory to prevent a memory leak.

Generally, close attention must be paid to the nature of the parameter of the exception handler, to make sure that exception: dynamically generated dynamically generated exceptions are deleted once the handler has processed them. Of course, when an exception is passed on to an outer level exception handler, the received exception should not be deleted by the inner level handler.

Different kinds of exceptions may be thrown: char *s, ints, pointers or references to objects, etc.: all these different types may be used in throwing and catching exceptions. So, various types of exceptions may come out of a try-block. In order to catch all expressions that may emerge from a try-block, multiple exception handlers (i.e., catch-blocks) may follow the try-block.

The order in which the exception handlers are placed is important. When an exception is thrown, the first exception handler matching the type of the thrown exception is used and remaining exception handlers are ignored. So only one exception handler following a try-block will be executed. Consequently, exception handlers should be placed from the ones having the most specific parameters to the ones having more general parameters. For example, if exception handlers are defined for char *s and void *s (i.e., any old pointer) then the exception handler for the former exception type should be placed before the exception handler for the latter type:

    try
    {
        // throws all kinds of pointers
    }
    catch (char const *message)
    {
        // processing thrown char pointers
    }
    catch (void *whatever)
    {
        // processing all other thrown pointers
    }
As an alternative to constructing different types of exception handlers for different types of exceptions, it is of course also possible to design a specific class whose objects contain information about the reason for the exception. Such an approach was mentioned earlier, in section 8.3.1. Using this approach, there's only one handler required, since we know we won't throw other types of exceptions:
    try
    {
        // code throws only Exception pointers
    }
    catch (Exception *e)
    {
        e->process();
        delete e;    
    }
The use of the delete e statement in the above code is an indication that the Exception object was created dynamically.

When the code of an exception handler that is placed beyond a try-block has been processed, the execution of the program continues beyond the last exception handler following that try-block (unless the handler uses return, throw or exit() to leave the function prematurely). Therefore, we distinguish the following cases:

In all cases a throw-statement will result in ignoring all remaining statements of the try-block in which the exception was thrown. However, destructors of objects defined locally in the try-block are called, and they are called before any exception handler's code is executed.

The actual computation or construction of the exception may be performed in various degrees of sophistication. Several possibilities are to use a plain new operator; to use static member functions of a class; to return a pointer to an object; or to use objects of classes derived from a class, possibly involving polymorphism.

8.5.1: The default catcher

In cases where different types of exceptions can be thrown, only a limited set of handlers may be required at a certain level of the program. Exceptions whose types belong to that limited set are processed, all other exceptions are treated differently, i.e., they are passed on to an outer level of exception handling.

An intermediate kind of exception handling may be implemented using the default exception handler, which should (due to the hierarchical nature of exception catchers, discussed in section 8.5) be placed beyond all other, more specific exception handlers. In this case, the current level of exception handling may do some processing by default, but will then, using the the empty throw statement (see section 8.3.1), pass the thrown exception on to an outer level. Here is an example showing the use of a default exception handler:

    #include <iostream>
    using namespace std;

    int main()
    {
        try
        {
            try
            {
                throw 12.25;    // no specific handler for doubles
            }
            catch (char const *message)
            {
                cout << "Inner level: caught char const *\n";
            }
            catch (int value)
            {
                cout << "Inner level: caught int\n";
            }
            catch (...)
            {
                cout << "Inner level: generic handling of exceptions\n";
                throw;
            } 
        }
        catch(double d)
        {
            cout << "Outer level still knows the double: " << d << endl;
        }
    }
    /*
        Generated output:
    Inner level: generic handling of exceptions
    Outer level still knows the double: 12.25
    */
From the generated output we may conclude that an empty throw statement throws the received exception to the next (outer) level of exception catchers, keeping the type and value of the exception: basic or generic exception handling can thus be accomplished at an inner level, specific handling, based on the type of the thrown expression, can then continue at an outer level.

8.6: Declaring exception throwers

Functions that are defined elsewhere may be linked to code that use these functions. Such functions are normally declared in header files, either as stand alone functions or as member functions of a class.

These external functions may of course throw exceptions. The declaration of such functions may contain a function throw list, in which the types of the exceptions that can be thrown by the function are specified. For example, a function that may throw `char *' and `int' exceptions can be declared as

void exceptionThrower() throw(char *, int);

A function throw list may appear after the function header (including a possible const specifier), and, noting that the throw list may be empty, it has the following generic form: throw([type1 [, type2, type3, ...]])

If a function doesn't throw exceptions an empty function throw list may be used. E.g.,

void noExceptions() throw ();

In this case the function definition must contain the empty function throw list too.

A function for which a function throw list is specified may not throw other types of exceptions. A run-time error occurs if it tries to throw other types of exceptions than mentioned in the function throw list.

If a function throw list is specified in the declaration, it must also be given in the definition of the function. For example, using declaration and definition in the next example:

    #include <iostream>
    using namespace std;

    void charPintThrower() throw(char const *, int);    // declarations

    class Thrower
    {    
        public:
            void intThrower(int) const throw(int);  
    };

    void Thrower::intThrower(int x) const throw(int)    // definitions
    {
        if (x)
            throw x;
    }
    
    void charPintThrower() throw(char const *, int)
    {
        int x;

        cerr << "Enter an int: ";
        cin >> x;
    
        Thrower().intThrower(x);
        throw "this text is thrown if 0 was entered";
    }

    void runTimeError() throw(int)
    {
        throw 12.5;
    }
    
    int main()
    {
        try
        {
             charPintThrower();
        }    
        catch (char const *message)
        {
            cerr << "Text exception: " << message << endl;
        }
        catch (int value)
        {
            cerr << "Int exception: " << value << endl;
        }
        try
        {
            cerr << "Up to the run-time error\n";
            runTimeError();
        }
        catch(...)
        {
            cerr << "not reached\n";
        }
    }
In the function charPintThrower() the throw statement clearly throws a char const *. However, since intThrower() may throw an int exception, the function throw list of charPintThrower() must also contain int.

If the function throw list is not used, the function may either throw exceptions (of any kind) or not throw exceptions at all. Without a function throw list the responsibility of providing the correct handlers is in the hands of the designer of the program.

8.7: Iostreams and exceptions

The C++ I/O library was used well before exceptions were available in C++. Hence, normally the classes of the iostream library do not throw exceptions. However, it is possible to modify that behavior using the ios::exceptions() member function. This function has two overloaded versions: The exception that is thrown is an object of the class ios::failure, derived from ios::exception. A failure object can be constructed with a string const &message, which can be retrieved using the virtual char const *what() const member.

Exceptions should be used for truly exceptional situations. Therefore, we think it is questionable to have stream objects throw exceptions for rather standard situations like EOF. Considering exceptions when input errors occur might be defensible, for example when input errors should not occur and imply a corrupted file. But here we think aborting the program with an appropriate error message usually is a more appropriate line of action. In any case, here is an example showing the use of exceptions in an interactive program, expecting numbers:

    #include <iostream>
    using namespace::std;

    int main()
    {
        cin.exceptions(ios::failbit);

        while (true)
        {
            try
            {
                cout << "enter a number: ";

                int value;

                cin >> value;
                cout << "you entered " << value << endl;
            }
            catch (ios::failure const &problem)
            {
                cout << problem.what() << endl;
                cin.clear();
                string s;
                getline(cin, s);
            }
        }
    }