- Next chapter
- Previous chapter
- Table of contents

# Chapter 12: 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. Mail to Frank Brokken or use an e-mail form. Please state the concerned document version, found in the title.

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

- The function may notice the abnormality and issue a message. This is probably the least disastrous reaction a program may show.
- The function in which the abnormality is observed may decide to stop its intended task, returning an errorcode to its caller. This is a great example of postponing decisions: now the calling function is faced with a problem. Of course the calling function may act similarly, by passing the error-code up to its caller.
- The function may decide that things are going out of hand, and may call `exit()` to terminate the program completely. A tough way to handle a problem.
- The function may use a combination of the functions `setjmp()` and longjmp()) to enforce non-local exits. This mechanism implements a kind of `goto` jump, allowing the program to proceed at an outer section, skipping the intermediate levels which would have to be visited if a series of returns from nested functions would have been used.

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 are 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 the use of exceptions.

## 12.1: Using exceptions: an outline

Using exceptions, it involves the following syntactical elements:

- `try`. The `try`-block surrounds statements in which exceptions may be generated (the parlance is for exceptions to be thrown). Example:

          try
          {
              // statements in which
              // exceptions may be thrown
          }

- `throw`: followed by an expression of a certain type, throws the expressionvalue as an exception. The `throw` statement should be executed somewhere within the `try`-block: either directly or from within a function called directly or indirectly from the `try`-block. Example:

          throw "This generates a char * exception";

- `catch`: Immediately following the `try`-block, the `catch`-block receives the thrown exceptions. Example of a `catch`-block receiving `char *` exceptions:

          catch (char *message)
          {
              // statements in which
              // the thrown char * exceptions
              // are processed
          }

## 12.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()` function an `Inner` object is allocated. After allocating the `Inner` object, its memberfunction `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.h>

    class Inner
    {
        public:
            Inner();
            ~Inner();
            void fun();
    };

    class Outer
    {
        public:
            Outer();
            ~Outer();
            void fun();
        private:
    };

    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();
    }

This program can be compiled and run, producing the following output:

        Outer constructor
        Inner constructor
        Outer fun
        Inner fun
        Inner destructor
        Outer destructor

This 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.

### 12.2.1: No exceptions: the setjmp() and longjmp() approach

In order to use `setjmp()` and `longjmp()` the basic program from section 12.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 returnvalue 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 returnvalue of the program is zero if only we would have been 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 returnvalue. 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 returnvalue 1.

Now try to follow these steps by studying the next program source, modified after the basic program given in section 12.2:

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

    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);
    }

Running the above program produces the following output:

        Outer constructor
        Inner constructor
        Outer fun
        Inner fun()
        Outer destructor

As will be clear from this output, the destructor of the class `Inner` is not executed. This is a direct result of the non-local characteristic of the call to `longjmp()`: from the function `Inner::fun()` processing continues immediately in the function `setjmp()` in `main()`: the call to `Inner::~Inner()`, hiddenly placed at the end of `Outer::fun()` is never executed.

Since the destructors of objects can easily be skipped when `longjmp()` and `setjmp()` are used, it's probably best to skip these function completely in C++ program.

### 12.2.2: Exceptions: the preferred alternative

In C++ exceptions are the best alternative to using `setjmp()` and `longjmp()`. In this section an example using exceptions is presented. Again, the program is derived from the basic program, given in section 12.2. The syntax of exceptions will be covered shortly, so please skip over the syntactical peculiarities like `throw, try` and `catch`. Here comes the sourcetext:

    #include <iostream.h>

    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 (...)
        {}
    }

In this program an exception is thrown, where a `longjmp()` was used in the program in section 12.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, like section 12.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.

Now look at the output generated by this program:

        Outer constructor
        Inner constructor
        Outer fun
        Inner fun
        Inner destructor
        Outer destructor

Note that the destructor of the `Inner` object, created in `Outer::fun()` is now called again. On the other hand, execution of the function `Inner::fun()` really terminates at the `throw` statement: the insertion of the text into `cout`, just beyond the `throw` statement, isn't performed.

So, with our illustrations we hope to have raised your appetite for exceptions by showing that

- Exceptions provide a means to break out of the normal flow control without having to use a cascade of `return`-statements, and without having to terminate the program.
- Exceptions do not disrupt the activation of destructors, and are therefore strongly preferred over the use of `setjmp()` and `longjmp()`.

## 12.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 source 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.h>
    #include <string>

    class Object
    {
        public:
            Object(string name)
            :
                name(name)
            {
                cout << "Object constructor of " << name << "\n";
            }
            Object(Object const &other)
            :
                name(other.name + " (copy)")
            {
                cout << "Copy constructor for " << name << "\n";
            }
            ~Object()
            {
                cout << "Object destructor of " << name << "\n";
            }
            void fun()
            {
                Object
                    toThrow("'local object'");

                cout << "Object fun() of " << name << "\n";
                throw toThrow;
            }
            void hello()
            {
                cout << "Hello by " << name << "\n";
            }
        private:
            string
                name;
    };

    int main()
    {
        Object
            out("'main object'");

        try
        {
            out.fun();
        }
        catch (Object o)
        {
            cout << "Caught exception\n";
            o.hello();
        }
    }

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 somewhat more closely. The member function `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'

Then 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. Next, the exception is processed. The local object is destroyed, and the catcher catches an `Object`, which again is a value parameter. Hence, another copy is created. We see the following lines:

    Object destructor of 'local object'
    Copy constructor for 'local object' (copy) (copy)

Now the message inside the catcher is displayed, and the hello member of the object received by the catcher is called, showing us once again that we received a copy of the copy of the local object of the `fun()` member function:

    Caught exception
    Hello by 'local object' (copy) (copy)

Now the program terminates, and the still living objects are 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 implemented so as to receive a reference to an object (`catch (Object &o)`), 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'

showing that only a single copy of the local object is 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, taking care that the generated object is properly deleted when the generated exception is caught.

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 syntax 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. A bit more sophistication would allow the use of variables, and non-existing variables may be referred to.

Each of these situations are enough reason to terminate the processing of the expression at hand, but there's no need to terminate the program. Each component of the processing of the expression may therefore throw an exception. E.g.,

        ...
        if (parse(expressionBuffer)) // parsing failed ?
            throw "Syntax error in expression";
        ...
        if (lookup(variableName))
            throw "Variable not defined";
        ...
        if (illegalDivision())
            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 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);

### 12.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's not possible to process the exception at the middle level, it's 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 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))
                handleTheMessage(exception);
            else
                throw;
        }

As we will see below (section 12.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? Suppose we have a class `Exception`, containing a memberfunction `Exception::Type Exception::severity()`. This memberfunction 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 memberfunction `toString()`, telling us a bit more about the nature of the generated exception. This smells a lot like polymorphism, showing `process()` as a virtual function for the derived classes `Message, Warning, Mistake, Error` and `Fatal`.

Now the 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 *e)
        {
                        // show the plain-text information
            cout << e->toString() << endl;

                                // Can we process it ?
            if (e->severity <= Exception::Warning)
                e->process();   // It's either a message
                                // or a warning
            else
                throw;          // No, pass it on
        }

Due to polymorphism, `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.

## 12.4: The try block

The `try`-block surrounds statements in which exceptions may be thrown. As we have seen, the actual `throw` statement doesn't have to be placed within the `try`-block, but may be placed in a function which is called from 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 `main()`'s `try`-block, 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 12.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 process (uncaught) exceptions is used, which is usually to abort the program. Try to compile and run the following tiny program, and see what happens:

        int main()
        {
            throw "hello";
        }

## 12.5: Catching exceptions

The `catch`-block contains code that is executed when an exception is thrown. Since expressions are thrown, the `catch`-block should know what kind of exceptions it should handle. Therefore, the keyword `catch` is followed by a parameter list having one parameter, which is of the type of the expression of the thrown exception.

So, an exception handler for `char *` exceptions will have the following form:

        catch (char const *message)
        {
            // code to handle the message
        }

Earlier (section 12.3) we've seen that such a message doesn't have to be thrown as 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 to be thrown as an exception dynamically, the exception handler will normally have to delete the allocated memory lest memory leaks away.

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

Different exception types may be thrown: `char *\`s, `int`s, pointers or references to objects, etc.: all these different types may be used in throwing and catching exceptions. So, the exceptions appearing at the end of a `try`-block may be of different types. In order to catch all the types that may appear at the end of 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 selected, remaining exception handlers are skipped. 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
        {
            // code may throw char pointers
            // and other pointers
        }
        catch (char *message)
        {
            // code processing the char pointers
            // thrown as exceptions
        }
        catch (void *whatever)
        {
            // code processing all other pointers
            // thrown as exceptions
        }

An alternative to construct different types of exception handlers for different types of situations, 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 discussed earlier, in section 12.3.1. Using this approach, there's only one handler required, since we know we won't throw other types of exceptions:

        try
        {
            // code may throw only
            // Exception pointers
        }
        catch (Exception *e)
        {
            // code processing the Exception pointer
            delete e;
        }

The use of the `delete e` statement in the above code indicates that the `Exception` object which could be thrown as an exception in the `try`-block 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). So we have the following cases:

- If no exception was thrown within the `try`-block no exception handler is activated, and the execution continues from the last statement in the `try`-block to the first statement beyond the last `catch`-block.
- If an exception was thrown within the `try`-block but neither the current level nor an other level contains an appropriate exception handler, the program's default exception handler is called, usually aborting the program.
- If an exception was thrown within the `try`-block and an appropriate exception handler is available, then that the code of that exception handler is exectuted. Following the execution of the code of the exception handler, the execution of the program continues at the first statement beyond the last `catch`-block.

In all cases a `throw`-statement will result in skipping 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 construction of the `Exception` object may be performed in various degrees of sophistication. Possibilities are using a plain `new` operator, using static memberfunctions of the class `Exception` dedicated to a particular kind of exception, returning a pointer to an `Exception` object, or using objects of classes derived from the `class Exception`, possibly involving polymorphism.

### 12.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 to be processed, all other exceptions are treated differently, e.g., they are passed on to an outer level of exception handling.

This situation is implemented using the default exception handler, which will (because of the reason given in the previous section 12.5) be placed beyond all other, more specific exception handlers. Often the default exception handler will be used in combination with the empty throw statement, discused in section 12.3.1.

Here is an example showing the use of a default exception handler:

        try
        {
            // this code may throw
            // different types of
            // exceptions
        }
        catch (char *message)
        {
            // code to process
            // char pointers
        }
        catch (int value)
        {
            // code to process
            // ints
        }
        catch (...)
        {
            // code to process other exceptions,
            // often passing the exception on
            // to outer level exception handlers:

            throw;
        }

The reason for passing unspecified exceptions on to outer level exception handlers is simply the fact that they are unspecified: how would you process an exception if you don't know its type? In these situations the outer level exception handlers should of course know what exceptions other than `char *`s and `int`s to expect....

## 12.6: Declaring exception throwers

Functions that are defined elsewhere may be linked to code using those functions. These functions are normally declared in header files, either as stand-alone functions or as member-functions of a class.

These external function 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 for which a function throw list was specified is not allowed to throw other types of exceptions. A run-time error occurs if it does 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 same example:

        #include <iostream>

        void intThrower() throw(int);
        void charP_IntThrower() throw (char *, int);

        void intThrower(int x) throw (int)
        {
            if (x)
                throw x;
        }

        void charP_IntThrower() throw (char *, int)
        {
            int
                x;
            cout << "Enter an int: ";
            cout.flush();
            cin >> x;

            intThrower(x);
            throw "from charP_IntThrower() with love";

        }

        int main()
        {
            try
            {
                 charP_IntThrower();
            }
            catch (char *message)
            {
                cout << "Text exception: " << message << endl;
            }
            catch (int value)
            {
                cout << "Int exception: " << value << endl;
            }
            return (0);
        }

In the function `charP_IntThrower()` the `throw` statement clearly throws a `char *`. However, since `IntThrower()` may throw an `int` exception, the function throw list of `charP_IntThrower()` must also contain `int`. Try this: remove the `int` from the (two!) function throw lists, compile and link the program and see what happens if you enter the value 5.

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

`void noExceptions() throw ();`
Again, the function definition must also contain the empty function throw list in this case.

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 all responsibilities of providing the correct handlers is in the hands of the designer of the program....

- Next chapter
- Previous chapter
- Table of contents