OSE - C++ Library User Guide

Graham Dumpleton
Dumpleton Software Consulting Pty Limited
PO BOX 3150
Parramatta, 2124
N.S.W, Australia
email: grahamd@nms.otc.com.au

Table of Contents

Guide to C++ Classes
OSE - C++ Library User GuideGuide to C++ Classes

Guide to C++ Classes

1 Message Log Facility

Runtime errors will inevitably occur in any complex software system. Independent of what may be done to recover from the error, it is important that information about the nature of the error be recorded to allow a human operator to later ascertain whether the software or the operating environment needs to be modified.

A mechanism for recording of textual information pertaining to an error is provided by the OTC_Logger class. Error messages can be logged at a number of priorities, with messages being displayed to the standard error output. If required, error messages can also be saved to a file.

By deriving from the OTC_Logger class you can capture the error messages and optionally save or cause them to be displayed using an alternate mechanism. For example, you may wish to have messages passed on to the UNIX syslog facility.

When messages are passed to the OTC_Logger class, they must have been preformatted. You could preformat messages using the sprintf() function or the ostrstream class. An alternative mechanism, is to use the OTC_LogStream class. This class provides a streams style interface for logging of messages. Messages will automatically be passed to the OTC_Logger class on the completion of each line of output.

If needing to output the name of the code file, and line in the code file from which a message is being generated, the OTCLIB_WARNING() macro is available.

2 Raising Exceptions

The C++ language provides a mechanism to handle errors through the exception facility. This includes the ability to throw and catch exceptions, and provide a special action to be performed in the event than an exception is not caught and dealt with. This last aspect of the exception facility comes in the form of library functions terminate() and set_terminate() in the standard C++ library.

Not all C++ compilers yet provide the exception facility. To make code portable between C++ compilers which do and do not provide the exception facility, raising of exceptions is hidden behind a set of macros and functions. If the C++ compiler supports exceptions, a true exception will be thrown. When a C++ compiler does not implement exceptions, the terminate() and set_terminate() functions are provided. When an exception needs to be raised it is handled as though it were not caught and thus the terminate() function is called to the halt the application.

The macros for raising of exceptions are OTCLIB_EXCEPTION(), OTCLIB_ASSERT() and OTCLIB_ENSURE(). When true exceptions need to be raised, the exception types which are thrown are OTC_Exception, OTCERR_AssertionFailure and OTCERR_PreconditionFailure. In addition to these, the OTCERR_OutOfMemory exception type exists for signalling of an out of memory condition. When the exception facility is not available, information about an exception will be displayed using the OTC_Logger class prior to the terminate() function being called.

3 Exception Cleanup

In addition to the ability to raise exceptions, a means of cleaning up after the exception is needed. The set_terminate() function provides a mechanism to perform one action when an exception is not caught. Use of the OTC_TObject class in conjunction with the set_terminate() function provides for multiple actions to be performed. The OTC_TObject class is a mixin class for any objects which need to provide a cleanup action in the event of program termination due to an unexpected error.

When an exception occurs, and the stack unwound, only destructors for objects created on the stack are invoked. If you had created an object using operator new(), and only held a pointer to that object via a stack variable, the object will not be destroyed, resulting in a memory leak. Three different classes are provided to assist in ensuring that objects allocated using operator new() or malloc() are destroyed when an exception occurs. These classes are OTC_Reaper, OTC_VecReaper and OTC_MallocReaper.

4 Program Debugging

When you are debugging a program, you may find it useful to know when specific functions are being entered and exited and the value of any arguments passed to those functions. The OTC_Tracer class, provides a mechanism for you to compile into your program, code which when run, will display information for you as a function is entered and exited, and the value of any arguments passed to the function. The OTC_Tracer class also allows you to generate debugging information about what a function is doing as it runs.

Using two supplied macros, OTCLIB_MARKBLOCK() and OTCLIB_TRACER(), debugging information can be tagged with different levels indicating verbosity. How much information is displayed, based on that tag, can be specified when the program is run. The macros also allow debugging code to be conditionally compiled into code without the need for unsightly `#if' preprocessor checks.

5 Module Debugging

The OTC_Tracer class, and the OTCLIB_MARKBLOCK() and OTCLIB_TRACER() macros provide a general facility only, for tracing program execution. In particular, they do not provide a way of tracing execution while in a specific module, or part of an application. To provide a mechanism for tracing portions of an application, additional classes and macros are provided. The classes concerned are OTC_TraceSwitch, OTC_TraceTag and OTC_TracePattern. When used in conjunction with the OTCLIB_MARKBLOCK() and OTCLIB_TRACER() macros, the classes provide the ability to trace code by module, function, the directory code is found in, or any other grouping which you desire.

6 Memory Management

The standard malloc() library tends to be inadequate for C++. Problems often arise from the library's inability to handle rapid creation and deletion of large numbers of small objects. To improve performance it is necessary to resort to special per class or common memory pool allocators. Such allocators can be constructed using the OTC_Pool class. To improve general performance, many small objects used in the library are automatically allocated using a common memory pool implemented by the OTC_CommonPool class. The ability for small objects to be allocated from the common memory pool can be added to classes through derivation from the OTC_MPObject class.

When related objects of a specific type are all to be deleted at the same time, it is possible to cluster those objects together into a single block of memory. This ability is provided by the OTC_Cluster and OTC_MCObject classes. Allocation of memory for clustered objects is handled by the OTC_Arena class. The OTC_Arena class may also be used directly to implement other memory allocators as may be the OTC_Heap class. The difference between these two classes being that memory from the OTC_Arena class must all be deleted at the same time. With the OTC_Heap class, selected blocks of memory may be returned to the heap class for reuse.

When creating memory allocators, correct alignment of memory is important. To assist in determining the alignment requirements of the builtin types and pointers the OTC_Alignment class is provided. An alternative to writing specialised memory allocators is to avoid, if possible, the creation of objects unless they are actually accessed by an application. Implementation of this approach to reducing memory use is assisted by the OTC_Ptr and OTC_VecPtr classes.

7 Resource Management

C++ does not provide garbage collection facilities. This forces you to deal with the problem of resource management. In particular, you must keep track of all objects allocated on the heap and know when they are no longer required. If object use is localised, this is not generally a problem. However, in a large system it is possible for objects to be referenced from quite distinct parts of an application. To determine if it is safe to delete an object, is not always a trivial task.

One scheme for which support is provided for is the technique of reference counting. Through either automatic or manual means, the number of parties interested in a specific object is recorded. When no parties are interested in the object, the object may be deleted. The class underlying this mechanism is OTC_Resource. This class provides a manual mechanism for performing reference counting. The mechanism can be partially automated by using the smart pointer class OTC_CResPtr and OTC_ResPtr.

The OTC_Resource class works through the reference count being embedded within the object of interest. If the class for the object cannot be modified, or you wish to perform reference counting on builtin types or arrays of objects, the classes above cannot be used. For these cases, alternative smart pointer classes are provided which maintain the reference count external to the object. These classes are OTC_CCtrPtr, OTC_CtrPtr, OTC_CCtrVecPtr and OTC_CtrVecPtr. For all of the above classes, the object of interest must have been created on the free store using `operator new()'.

8 Linked Lists and Trees

9 Iterators and Applicators

10 Vectors, Stacks and Queues

11 Lists and Deques

12 Sets, Bags and Maps

13 Strings and Symbols

Although C and C++ allow strings of characters to be stored through use of the `char*' type, it is a long way from having a type for strings which is directly supported by the language. With C++ we can solve a lot of the problems which occur when using the `char*' type, or C strings as they are often called, by encapsulating operations which we would like to perform on character strings into a class.

There are two principal classes provided for use in place of C strings. These classes are OTC_String and OTC_Symbol. Both classes provide you with the ability to store character strings containing embedded null characters. Both will also place a null guard byte at the end of the string, in case there is no explicit null terminator. The addition of the null terminator allows the string to be used where C strings would have been used.

In addition to the two main classes, a number of support classes exist. For string type objects where derivation from OTC_String is not a viable option, the OTC_SObject class exists. When used as a base class, this allows your classes to be used nearly everywhere that OTC_String can be passed as argument, but doesn't give access to the string for editing.

Both OTC_String and OTC_Symbol are internally implemented using OTC_RString. This class encapsulates the delayed copy and buffering mechanisms. The OTC_Length and OTC_Capacity classes exist to provide a means of specifying the length and/or capacity of a string at the time of creation. In addition, the OTC_CString class is provided to allows strings to be easily created which only use as much memory as they need. That is, the buffering mechanism is bypassed at the point the string is created.

A final class, OTC_TString, exists to allows the OTC_String class to have an automatic conversion operator for the type `char const*', but still provide a measure of safety for addition of strings using an overloaded `operator+()'. The problem being avoided being that of the lifetime of the string returned by `operator+()' when the result is saved as a pointer of type `char const*'.

14 Pattern Matching and Records

15 Program Arguments

There is no method for accessing a programs arguments, or the name of the program, except within the `main()' routine for that program. This means that if you need to access this information from an arbitrary point in the program, you need to have saved away the information in a set of global variables whilst in the `main()' routine.

For those instances where it would be valid to access this information, a number of classes are provided to make access to the information consistant, and to also assist in manipulating that information. The class provided to assist in manipulating command line options, obtained from any source, is the OTC_Options class. The classes provided for making access to the programs arguments consistant, are the OTC_Program and OUX_Program classes.

16 Interfacing to the File System

17 Interfacing to UNIX

A number of classes are provided which are specific to the UNIX operating system. These relate to aspects of the environment in which an application is executing, or features available in the environment for controlling a process. The first group of classes are OUX_User and OUX_Group. These provide information about the user and group a process is running as. They also allow information about arbitrary users and groups to be obtained. The next class is OUX_SignalBlock. This class provides a portable mechanism for blocking signals on different variants of the UNIX operating system.

18 Event Driven Systems

For some applications, it is necessary for concurrent tasks to be executing. Due to the minimal amount of data associated with the tasks, or the need to share data between tasks, the use of separate heavyweight processes is not always viable. One solution is to use threads, a feature provided by the operating system for executing multiple tasks within the one process. The writing of code which executes safely within a threaded environment is difficult. Also, not all operating systems provide threads and where they do, the implementations often differ, making portability of code a problem.

A solution often adopted is to write the application in the style of an event driven system. That is, something happening and to which a task should respond, is modelled as an event. The event is put together by a central thread of control, with then event and thread of execution being handed over to the appropriate task. It is now the tasks job to deal with that event and subsequently return the thread of execution back to the executive or dispatcher.

The class which gives over control to each of the tasks when an event for that task arrives is OTC_Dispatcher. To implement simulation type systems, the OTC_Job class is also provided. At a higher level, the OTC_EVAgent and OTC_Event classes are provided for implementing event based systems. The underlying job queue used by the dispatcher is OTC_JobQueue. This class is extended by OUX_JobQueue and OTK_JobQueue to handle real time events within the UNIX operating system. The latter of of these also handles the TK GUI library. The types of events which can be used when using the job queues supporting real time semantics are OTCEV_Action, OTCEV_Alarm, OTCEV_Timeout, OTCEV_IOEvent and OUXEV_Signal.