Issue #019
January, 1997


Contents:

Placement New/Delete
Introduction to Exception Handling Part 5 - terminate() and unexpected()
Notes From ANSI/ISO - Recent Changes to terminate() and unexpected()
Introduction to STL Part 6 - Stacks


PLACEMENT NEW/DELETE

In C++, operators new/delete mostly replace the use of malloc() and
free() in C.  For example:

        class A {
        public:
                A();
                ~A();
        };

        A* p = new A;

        ...

        delete p;

allocates storage for an A object and arranges for its constructor to
be called, later followed by invocation of the destructor and freeing
of the storage.  You can use the standard new/delete functions in the
library, or define your own globally and/or on a per-class basis.

There's a variation on new/delete worth mentioning.  It's possible to
supply additional parameters to a new call, for example:

        A* p = new (a, b) A;

where a and b are arbitrary expressions; this is known as "placement
new".  For example, suppose that you have an object instance of a
specialized class named Alloc that you want to pass to the new
operator, so that new can control allocation according to the state of
this object (that is, a specialized storage allocator):

        class Alloc {/* stuff */};

        Alloc allocator;

        ...

        class A {/* stuff */};

        ...

        A* p = new (allocator) A;

If you do this, then you need to define your own new function, like
this:

        void* operator new(size_t s, Alloc& a)
        {
                // stuff
        }

The first parameter is always of type "size_t" (typically unsigned
int), and any additional parameters are then listed.  In this example,
the "a" instance of Alloc might be examined to determine what strategy
to use to allocate space.  A similar approach can be used for operator
new[] used for arrays.

This feature has been around for a while.  A relatively new feature
that goes along with it is placement delete.  If during object
initialization as part of a placement new call, for example during
constructor invocation on a class object instance, an exception is
thrown, then a matching placement delete call is made, with the same
arguments and values as to placement new.  In the example above, a
matching function would be:

        void operator delete(void* p, Alloc& a)
        {
                // stuff
        }

With new, the first parameter is always "size_t", and with delete,
always "void*".  So "matching" in this instance means all other
parameters match.  "a" would have the value as was passed to new
earlier.

Here's a simple example:

        int flag = 0;

        typedef unsigned int size_t;

        void operator delete(void* p, int i)
        {
                flag = 1;
        }

        void* operator new(size_t s, int i)
        {
                return new char[s];
        }

        class A {
        public:
                A() {throw -37;}
        };

        int main()
        {
                try {
                        A* p = new (1234) A;
                }
                catch (int i) {
                }
                if (flag == 0)
                        return 1;
                else
                        return 0;
        }

Placement delete may not be in your local C++ compiler as yet.  In
compilers without this feature, memory will leak.  Note also that you
can't call overloaded operator delete directly via the operator
syntax; you'd have to code it as a regular function call.


INTRODUCTION TO EXCEPTION HANDLING PART 5 - TERMINATE() AND UNEXPECTED()

Suppose that you have a bit of exception handling usage, like this:

        void f()
        {
                throw -37;
        }

        int main()
        {
                try {
                        f();
                }
                catch (char* s) {
                }

                return 0;
        }

What will happen?  An exception of type "int" is thrown, but there is
no handler for it.  In this case, a special function terminate() is
called.  terminate() is called whenever the exception handling
mechanism cannot find a handler for a thrown exception.  terminate()
is also called in a couple of odd cases, for example when an exception
occurs in the middle of throwing another exception.

terminate() is a library function which by default aborts the
program.  You can override terminate if you want:

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

        typedef void (*PFV)(void);

        PFV set_terminate(PFV);

        void t()
        {
                cerr << "terminate() called" << endl;
                exit(1);
        }

        void f()
        {
                throw -37;
        }

        int main()
        {
                set_terminate(t);

                try {
                        f();
                }
                catch (char* s) {
                }

                return 0;
        }

Note that this area is in a state of flux as far as compiler
adaptation of new features.  For example, terminate() should really be
"std::terminate()", and the declarations may be found in a header file
"<exception>".  But not all compilers have this yet, and these
examples are written using an older no-longer-standard convention.

In a similar way, a call to the unexpected() function can be triggered
by saying:

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

        typedef void (*PFV)(void);

        PFV set_unexpected(PFV);

        void u()
        {
                cerr << "unexpected() called" << endl;
                exit(1);
        }

        void f() throw(char*)
        {
                throw -37;
        }

        int main()
        {
                set_unexpected(u);

                try {
                        f();
                }
                catch (int i) {
                }

                return 0;
        }

unexpected() is called when a function with an exception specification
throws an exception of a type not listed in the exception
specification for the function.  In this example, f()'s exception
specification is:

        throw(char*)

A function declaration without such a specification may throw any
type of exception, and one with:

        throw()

is not allowed to throw exceptions at all.  By default unexpected()
calls terminate(), but in certain cases where the user has defined
their own version of unexpected(), execution can continue.

There is also a brand-new library function:

        bool uncaught_exception();

that is true from the time after completion of the evaluation of the
object to be thrown until completion of the initialization of the
exception declaration in the matching handler.  For example, this
would be true during stack unwinding (see newsletter #017).  If this
function returns true, then you don't want to throw an exception,
because doing so would cause terminate() to be called.


NOTES FROM ANSI/ISO - RECENT CHANGES TO TERMINATE() AND UNEXPECTED()
Jonathan Schilling, jls@sco.com

Earlier in this issue the basic purposes of the terminate() and
unexpected() functions are described.  In the past year the standards
committee has made several refinements to these functions.

The committee has confirmed that direct calls may be made to these
functions from application code.  So for instance:

        #include <exception>
        ...
                if (something_is_really_wrong)
                        std::terminate();

This will terminate the program without unwinding the stack and
destroying local (and finally static) objects.  Alternatively, if you
just throw an exception that doesn't get handled, it is implementation-
dependent whether the stack is unwound before terminate() is called.
(Most implementations will likely support a mode wherein the stack is
not unwound, so that you can debug from the real point of failure).

Probably the main purpose of making direct calls to terminate() and
unexpected() will be to simulate possible error conditions in
application testing, especially when the application has established
its own terminate and unexpected handlers.

The committee has changed slightly the definition of what handlers are
used when terminate() or unexpected() are called.  In most cases, they
are now the handlers in effect at the time of the throw, which are not
necessarily the current handlers.  Usually they are one and the same,
but consider:

        #include <exception>

        void u1() { ... }
        void u2() { ... }

        class A {
        public:
                A() { ... }
                A(const A&) { ... std::set_unexpected(u2); }
        };

        void f() throw(int)
        {
                A a;
                throw a;        // which unexpected handler gets called?
        }

        int main()
        {
                std::set_unexpected(u1);
                f();
                return 0;
        }

The copy constructor for A is called as part of the throw operation in
f(), so by the time the C++ implementation determines that an
unexpected handler needs to be called, u2() is the current handler.
However, based on this recent change, it is the handler in effect at
the time of the throw - u1() - which gets called.  On the other hand,
if a direct call to terminate() or unexpected() is made from the
application, it is always the current handler which gets called.

Some would argue that this kind of rule just adds complexity without
much benefit to already-complex C++ implementations, but others feel
that if an application is going to be dynamically changing its
terminate and unexpected handlers, retaining the correct association
is important.

In the next issue we'll talk about another clarification of terminate()
and unexpected(), this time related to the uncaught_exception() library
function introduced above.


INTRODUCTION TO STL PART 6 - STACKS

We're nearly done discussing the basic data structures underlying the
Standard Template Library.  One more worth mentioning is stacks.  In
STL a stack is based on a vector, deque, or list.  An example of stack
usage is:

        #include <iostream>
        #include <stack>
        #include <list>

        using namespace std;

        int main()
        {
                stack<int, list<int> > stk;

                for (int i = 1; i <= 10; i++)
                        stk.push(i);

                while (!stk.empty()) {
                        cout << stk.top() << endl;
                        stk.pop();
                }

                return 0;
        }

We declare the stack, specifying the underlying type (int), and the
sort of list used to represent the stack (list<int>).

We then use push() to push items on the stack, top() to retrieve the
value of the top item on the stack, and pop() to pop items off the
stack.  empty() is used to determine whether the stack is empty or not.

We will move on to other aspects of STL in future issues.  One data
structure not discussed is queues and priority_queues.  A queue is
something like a stack, except that it's first-in-first-out instead of
last-in-first-out.


ACKNOWLEDGEMENTS

Thanks to Nathan Myers, Eric Nagler, David Nelson, and Jonathan
Schilling for help with proofreading.


SUBSCRIPTION INFORMATION / BACK ISSUES

To subscribe to the newsletter, send mail to majordomo@world.std.com
with this line as its message body:

subscribe c_plus_plus

Back issues are available via FTP from:

        rmi.net /pub2/glenm/newslett

or on the Web at:

        http://rainbow.rmi.net/~glenm

There is also a Java newsletter.  To subscribe to it, say:

subscribe java_letter

using the same majordomo@world.std.com address.

-------------------------

Copyright (c) 1997 Glen McCluskey.  All Rights Reserved.

This newsletter may be further distributed provided that it is copied
in its entirety, including the newsletter number at the top and the
copyright and contact information at the bottom.

Glen McCluskey & Associates
Professional C++ Consulting
Internet: glenm@glenmccl.com
Phone: (800) 722-1613 or (970) 490-2462
Fax: (970) 490-2463
FTP: rmi.net /pub2/glenm/newslett (for back issues)
Web: http://rainbow.rmi.net/~glenm
