Introduction to the Use of ZOOM Exceptions

Walter E. Brown, 30-Oct-1997, last revised 10-May-2000

Contents

  1. Introduction
  2. How to declare and define a new exception class
  3. Constructing/throwing an instance of a ZOOM exception
  4. Resulting log message
  5. Available severity levels
  6. Using handlers
  7. Available handler behaviors
  8. Using loggers
  9. Available logger behaviors
  10. An example
  11. ZMerrno: Exception history


1. Introduction

This summary describes the mechanics for creating and throwing a ZMexception (ZOOM exception class) object as implemented by the ZOOM Exceptions package.

Note that all public C++ symbols used within this Exceptions package begin with the unlikely prefix "ZMex" (or, in the case of the preprocessor, "ZMEX") in order to help avoid namespace pollution. For example, we use "ZMexception" as the name of the class from which all other exception classes are (directly or indirectly) derived.

Additionally, all ZOOM-generated ZMexception classes will use at least "ZMx" as their name prefix. More typically, to avoid internal name clashes, the names start with a short string identifying the package, e.g. "ZMxHep" for HepTuple, or "ZMxpv" for the PhysicsVectors package. It is recommended that users defining their own ZMexceptions establish and employ some similar convention.


2. How to declare and define a new exception class

Declaring/defining a new exception class is done as shown below (code adapted from "Exceptions/test/exctest1.cc"):
  // Required header, with recommended code guard:
  #ifndef ZMEXCEPTION_H
    #include "Exceptions/ZMexception.h"
  #endif  // ZMEXCEPTION_H

  // Required (sample) declaration (for use in a .h file):
  ZMexStandardDefinition( ZMexception, ZMxOops );
    // Defines class ZMxOops : public ZMexception { ... };

  // Required (sample) definition (for use in a .cc file):
  ZMexClassInfo ZMxOops::_classInfo( "Oops", "ExcTest", ZMexWARNING );
    // Provides certain details specific to the new class
In the example above:
The name (as it is to appear in the log), facility, and assigned severity can be modified after construction, as in these examples:

Defining exceptions when namespace is enabled

Libraries for ZOOM packages can be built with ZM_USE_NAMESPACES defined. If the ZMexceptions package is built with this defined, all global symbols will be placed in the namespace zmex.

This means that user code linking with the namespace-enambled version must properly qualify symbols used. The code may do this overall by declaring

  using namespace zmex;
or by individually qualifying each symbol needed from the Exceptions package:
  
  ZMexStandardDefinition( zmex::ZMexception, Whoops );
  zmex::ZMexClassInfo Whoops::_classInfo( "Whoops", "MyCode", ZMexERROR );
Two items of note here:
  1. ZMexStandardDefinition, being a macro, is not in the zmex namespace.
  2. As shown above, it is recommended that user code not place its defined exceptions in the zmex namespace.

When ZMthrow-ing ZMexceptions, note that ZMthrow and ZMthrow_from are macros, so they still appear as if in the global namespace:

  ZMthrow (Whoops("A goof")); 

It is possible to write code that will work whether or not namespaces are enabled. ZMenvironment.h provides two useful macros:


3. Constructing/throwing an instance of a ZOOM exception

This Exceptions package provides a global facility, ZMthrow(), to make use of ZMexception and its descendent classes. Before using this capability, insert the following line to provide the necessary declaration:
  #include "Exceptions/ZMthrow.h"

Thereafter, an exception of a class defined as shown above is typically constructed and thrown within a single statement, such as:

  ZMthrow( ZMxOops("Ouch!") );

Here, "Ouch!" may be arbitrary text to be associated with this particular occurrence (exception instance). The text will be logged, as described below. A second macro is provided for cases where it is preferable to supply some other line and file as the actual origin of the throw:

  ZMthrow_from( exception, line, file );

Constructing a ZMexception from an ostringstream

It may often be practical to use an ostringstream as a helper in forming the text of the message of a ZMException. For syntactic convenience, the ZMException class provides a constructor taking an ostringstream. And the ZMexStandardDefinition macro which helps create derived exceptions also provides this constructor.

For example, you can do:

  std::ostringstream oss;
  oss << "Ouch: " << n << " errors too many"; 
  ZMthrow( ZMxOops(oss) );


4. Resulting log message

Assuming that the ExcTest program has been compiled with appropriate compiler switches that enable use of C++ exceptions, the logged message resulting from the above ZMthrow(...) example will be:
  ExcTest-S-Oops [#1]
    Ouch!
    Wed Mar  4 17:18:55 1998
    -- ZMthrow was issued at line 25
    of file "/disk4/home/me/testarea/exctest1.cc"
    ... Exception thrown!
The parts of this message are interpreted as follows:

Influencing the content of the log message

Beyond the string argument to the constructor of a ZMexception, it is possible to influence (in advance) the content of the message produced when a particular ZMexception is ZMthrow'n.
  1. The exception name, facility, and severity levels can be altered relative to their original definitions, as described above.
  2. The time, date, and all directory portions of the file name can be omitted, as described below.
  3. The thrower can force some different file and line number information into the message by using ZMthrow_from instead of ZMthrow as illustrated here:

5. Available severity levels

Here is a list of the seven severity levels that are available for use in defining a new exception class. Each severity is accompanied by a description of its intended interpretation, as documented in the Exceptions/ZMexSeverity.h header file:

6. Using handlers

In the Exceptions package, a handler is the term for an instance of a class that processes a ZMthrow'n exception. A handler is responsible for having the exception instance logged, for taking any remedial action appropriate to the exception instance, and for determining whether the exception instance can safely be ignored by the user code.

The Exceptions package includes a pre-defined handler class, ZMexHandler. Instances of this class can be configured with any of a number of pre-defined handler behaviors (listed below).

Each exception class is associated with a handler to be applied to all ZMthrow'n instances of that class. By default, this handler implements the behavior known as ZMexHandleViaParent(); this applies the behavior -- whatever it may be -- of the parent exception class' handler to the current exception class.

A user may change this behavior in either of two ways. A different handler may be associated with an exception class when the class is defined:

  ZMexClassInfo ZMxOops::_classInfo(
    "Oops",
    "ExcTest",
    ZMexWARNING,
    ZMexHandler( ZMexIgnoreAlways() )
  );
Alternatively, the handler associated with an exception class may be changed dynamically:
  #include "Exceptions/ZMexHandler.h"
  // ...
  ZMexOops::setHandler( ZMexHandler( ZMexIgnoreAlways() ) );

The given behavior will apply to any exceptions ZMthrow'n after the handler has been established.


7. Available handler behaviors

Here is a list of the five standard handler behaviors that are defined via the Exceptions package.

8. Using loggers

In the Exceptions package, a logger is the term for an instance of a class that records, to a designated destination, a ZMthrow'n exception. A logger is responsible only for routing the message associated with an exception instance; it is not responsible for determining or formatting any message.

The Exceptions package includes a pre-defined logger class, ZMexLogger. Instances of this class can be configured with any of a number of pre-defined logger behaviors (listed below).

Each exception class is associated with a logger to be applied to all ZMthrow'n instances of that class. By default, this logger implements the behavior known as ZMexLogViaParent(); this applies the behavior -- whatever it may be -- of the parent exception class' logger to the current exception class.

A user may change this behavior in either of two ways. A different logger may be associated with an exception class when the class is defined:

  ZMexClassInfo ZMxOops::_classInfo(
    "Oops",
    "ExcTest",
    ZMexWARNING,
    ZMexHandler( ZMexIgnoreAlways() ),
    ZMexLogger( ZMexLogAlways() )
  );
Alternatively, the logger associated with an exception class may be changed dynamically:
  #include "Exceptions/ZMexLogger.h"
  // ...
  ZMexOops::setLogger( ZMexLogger( ZMexLogAlways() ) );
The given behavior will apply to any exceptions ZMthrow'n after the logger has been established, provided the handler invokes the logger.

9. Available logger behaviors

Here is a list of the standard logger behaviors that are defined via the Exceptions package.

10. An example

To illustrate the use of the previously-described handler and logger behaviors, we demonstrate the creation of an exception class that, when ZMthrow'n, will refrain from logging and simply throw (in the C++ sense). We will name this class ZMxCppStyle (code adapted from "Exceptions/test/exctest4.cc"):
  #ifndef ZMEXCEPTION_H
    #include "Exceptions/ZMexception.h"
  #endif  // ZMEXCEPTION_H

  // Declaration:
  ZMexStandardDefinition( ZMexception, ZMxCppStyle );

  // Definition:
  ZMexClassInfo ZMxCppStyle::_classInfo( "CppStyle", "ExcTest", ZMexERROR
                                       , ZMexHandler( ZMexThrowAlways() )
                                       , ZMexLogger ( ZMexLogNever() )
                                       );


11. ZMerrno: Exception history

This Exceptions package records, by default, all exceptions of severity ZMexERROR and above that have been ZMthrow'n. Known as ZMerrno, this capability is in addition to the logging described earlier, and allows user code to interrogate and make decisions based on such exceptions.

To use this ZMerrno facility, insert the following line to provide the necessary declaration:

  #include "Exceptions/ZMerrno.h"
This declaration makes the following operations (functions) available: