Take for example, an application which places locks on files which it is using. If this were to encounter some unrecoverable error mid way through an operation, it would need to be able to remove those locks and release the files for use by other applications. The problem with using exit() directly is that knowledge of how to remove the locks is most likely not known at the point where exit() is being invoked.
A complimentary function to exit() is available on some systems. This is called on_exit() or atexit(), and allows you to register a function which will be called when exit() is invoked. As this is not widespread, the terminate() and set_terminate() functions which were described previously, should be used instead when you are programming in C++. By using these functions, you are ensured that any cleanup actions you define will also be executed when an exception is thrown, but not caught, as this results in the terminate() function being called.
class OTC_TObjectIf any of your classes must do something special, if the program is terminated abnormally, you should derive your class from OTC_TObject and define the cleanup() function. When you create an instance of your class, it will be linked with any other classes, which also derive from the OTC_TObject class. The terminateAll() function, when called, will iterate through this list of classes and call the cleanup() function on each.
{
public:
static void terminateAll();
// Iterates through all instances of
// this class and invokes cleanup()
// on each instance.
protected:
virtual void cleanup() = 0;
// Should be redefined in a derived
// class to perform any actions which
// need to be done in the result of
// abnormal program termination.
};
In order that the terminateAll() function is invoked, when the terminate() function is called, you must register the terminateAll() function. This is done by calling the set_terminate() function.
#include <OTC/debug/trmnate.hh>An example of a class which would be derived from the TObject class is shown below. In this example the cleanup() function ensures that the open file is closed and all data still in the stream is written out. If the file had a lock on it, the cleanup() could also have removed the lock. This is not shown in the example though.
main()
{
set_terminate(OTC_TObject::terminateAll);
// ...
}
class EX_Log : private OTC_TObjectThe cleanup() function should do as little as necessary. It should not try and allocate any memory, as it may be because of memory exhaustion that the program was being terminated.
{
public:
EX_Log(char const* aFile) : myFile(aFile) {}
private:
void cleanup() { myFile.close(); }
ofstream myFile;
};
The example above shows the function terminateAll() being registered as the function to call when terminate() is invoked, normally however, this would not be done. The reason for this is that the terminateAll() function returns. If a function returns to the terminate() function from which it is called, the terminate() function will call abort(), which results in a `core' file being produced. Instead of registering terminateAll(), you should register a wrapper function which will call terminateAll() and then call exit(). For example:
void terminateApp()If your program should be restarted, rather than calling exit() you should execute your program again.
{
OTC_TObject::terminateAll();
exit(1);
}
main()
{
set_terminate(terminateApp);
// ...
}
A version of the terminateApp() function is supplied in the library. This function is called otclib_terminate_function() and as well as calling terminateAll() and exiting, will send a warning to the message log system indicating that the program is terminating. The definition of the function is:
void otclib_terminate_function()To use this function, add the call
{
OTC_TObject::terminateAll();
OTC_Logger::notify(
OTCLIB_LOG_ALERT,
"Program terminating");
exit(1);
}
set_terminate(otclib_terminate_function());at the start of your programs main() routine.
The concept behind these classes is that, after an object has been created on the heap, it is grabbed by an instance of one of the reaper classes. The reaper class is created on the stack. The code which could potentially throw an exception is called. If an exception occurred, the destructor of the reaper class would be called, resulting in the object on the heap being destroyed. After the point in the code where the exception could be thrown the reaper class is made to release the object on the heap. Provided the object is released, it would not be deleted via the reaper object when that code block exited.
An example of the OTC_Reaper class is:
void function()The OTC_Reaper and OTC_VecReaper classes are templates and are used respectively for pointers to a single object, and pointers to an array of objects. The OTC_MallocReaper is not a template, and is used for memory which has been allocated using the malloc() function.
{
OTC_Reaper<Object> xxxObject;
Object* theObject = new Object;
OTCLIB_ASSERT(theObject != 0);
xxxObject.grab(theObject);
... code which could throw an exception
xxxObject.release();
... code which subsequently saves away object
... or deletes the object
}