FXException Class Reference

#include <FXException.h>


Detailed Description

The base exception class in FOX.

FXException is the base class for exception throws in FOX and is probably one of the most feature rich exception classes on any toolkit for C++. Features include:

Usage is exceptionally easy with levels of support so you can automate as much or little of the generation process as you like. Typical forms include: Automatic retries are easy with try & catch replaced with macros:
FXERRH_TRY
{
   // do something
}
FXERRH_CATCH(FXException &e)
{
   FXERRH_REPORT(parentwnd, e)
}
FXERRH_ENDTRY
You should also replace throw with FXERRH_THROW() though this is usually done for you if you use any of the FXERRH or FXERRG macros.
See also:
FXERRHM(), FXERRHPTR(), FXERRGNF()

Extending exception types

You can subclass FXException to create your own specialised exception types - indeed, Tn does this as do applications I have written which use TnFOX. As of v0.87, FXException is now a destructively copied class rather than the normal exception class it previously was. This was done for two reasons:

  1. FXException grew substantially in size as of v0.87, such that when being thrown up a call stack its large size made copy construction slow, and thus throwing exceptions slow.
  2. There is a most unfortunate bug in MSVC whereby the compiler function stack size calculation routine misunderstands exception objects. It adds up the size of every exception potentially throwable in a function, so if you might FXERRHM()'d say five objects in your function, MSVC adds five times the size of the exception class to the total used. When your exception class is about 16Kb long, you exhaust stack space very, very quickly indeed!

Source munging

FXException has an accompanying source file munging script written in Python called CppMunge.py. This useful tool can automatically extract error code macros as specified in your C++ sources and assign them unique constants into a header file (by default called ErrCodes.h) that are guaranteed to be unique to each class name. This greatly simplifies error handling across entire projects, irrespective of libraries used. You still need to include "ErrCodes.h" in each of your source files using this feature. See this link for more details

Nested Exceptions

The C++ standard states that if an exception is thrown during a stack unwind caused by the handling of an exception throw, the program is to exit immediately via std::terminate() (ie; immediate exit without calling atexit() registrants or anything else). The rationale behind this is that destructors permanently destroy state and so cannot be guaranteed restartable.

This writer personally finds this to be a silly limitation substantially decreasing the reliability of C++ programs using exceptions. While correct design has the programmer wrapping all destructors capable of throwing in a try...catch section, a single missed wrapper causes immediate program exit - a situation which may arise once in a blue moon and thus be missed by testing. What is really needed is to change the C++ spec so that throwing any exception from any destructor is illegal - and then more importantly, have the compiler issue an error if code tries it. This would require modification of the linker symbol mangling system and thus break binary compatibility though.

Furthermore, this writer feels that as soon as you use ANY exceptions in C++, you must write all all destructors to be exception aware because as code grows in size, it becomes impossible to track whether code called by any non-trivial destructor throws or not. Best IMHO to assume that everything can throw, at all times. What annoys me most of all about the C++ standard action of immediately terminating the process is that there is no opportunity for saving out state or even providing debug information.

The C++ source code munger detailed above can optionally insert try...catch(FXException) wraps around each and every destructor it processes (this can be prevented via placing FXERRH_NODESTRUCTORMOD after the destructor definition). The wrap checks to see if an exception is currently being thrown for the current thread and if so, it appends the new exception and sets the FXERRH_HASNESTED and FXERRH_ISFATAL flags. Thus by default the program will report the error, clean up and exit.

Note:
The nested exception framework is disabled during static data initialisation and destruction.

Restarting destructors

It is possible to write restartable destructors in C++ - however, they won't always be of use to you eg; if the object is created on the stack. In that situation, an exception thrown implies unwinding the rest of the stack before it can be handled which clearly implies that that object can no longer exist in any useful sense.

An important point is that the next issue (time of writing: June 2003) of the C++ standard is likely to make all exception throwing from destructors illegal, so if you wish to future-proof your code you want to avoid it.

Static data initialisation and destruction:

A major headache I had was what to do when an exception is thrown during static data initialisation and destruction. Firstly, I'll tell you the received wisdom - just don't do it! - your application is in an undefined state because static data is initialised and destroyed in a random order, so you can't guarantee anything at all! The facilities provided by FX::FXProcess_StaticInit are there precisely to permit non-trivial static data initialisation and destruction, so use them!

However, sometimes you can't escape running non-trivial code and it's better to reuse the library rather than duplicate the functionality in non-throwing code. For this situation I've had FXException print its report to stderr on construction if static data is unavailable and it also disables its nested exception functionality (because it requires holding static state). This should cover informing the user of a problem rather than the generic and unhelpful message you usually get when terminate() gets called.

Good practice in exception using C++:

First cardinal rule of all is to always assume every line of code can throw an exception. Not trying to be clever will prevent nasty bugs!


The documentation for this class was generated from the following file:

(C) 2002-2008 Niall Douglas. Some parts (C) to assorted authors.
Generated on Fri Jun 13 21:55:23 2008 for TnFOX by doxygen v1.5.6