The OATH library supplies the basic mechanisms for supporting the parallel hierarchies of "OATH Accessors" (kin to smart pointers) and internal classes. These parallel class hierarchies permit implicit (automatic) memory management (reference counting and garbage collection) of its objects in a compiler-independent C++ class library.
The core OATH library provides many fundamental types such as bag, set, table, queue, seq, list, string, character, token, stringToken, and others. OATH supports heterogeneous container classes by providing a "safe cast" from base types to more derived types.
Note that OATH is a design strategy supported by the core OATH library. To effectively use the library, it helps to understand the strategy. See the paper "The Features of the Object-oriented Abstract Type Hierarchy (OATH)", initially published in the 1991 USENIX C++ Conference Proceedings, which is available in the OATH doc directory.
There are two groups of OATH users: those who use the OATH types and those who develop new OATH types. The OATH library was designed to make it very easy to use OATH types, but much more is expected from developers of new OATH types.
Since the OATH accessor lies between pointers and references, a new but similar syntax would be nice. For instance, '@' could be used instead of '*' or '&':
list@ L;
However, OATH is intended to be a C++ library, not a new language. So, the OATH accessor types are suffixed with a capital letter 'A':
listA L;
Analogous to pointers, constructing a listA constructs only an accessor, not the list itself. In the declaration above, L is initialized to access the Nil object. Given an existing listA K, then L could be initialized to access the same list object that K accesses:
listA L = K;
Note that both K and L access the same list. To make a new OATH object, a "make" function must be called. For instance, to initialize L to access a copy of the list accessed by K, the function makeCopy() can be used:
listA L = K.makeCopy();
To make an object from scratch, an implementation type must be chosen and its "make" function, a static member of the accessor class, must be called. For instance,
listA L = dlListA::make();
makes an empty dlList (doubly-linked list) and assigns it to the list accessor L.
In addition to static "make" functions, the accessor types also have a static member function isa(), which is the OATH "safe cast". For instance, given bagA B,
listA L = listA::isa(B);
attempts to "safe cast" B to a list and assign it to L. If B is not really a list, than isa(B) will return Nil.
Each type in the OATH library has a manual entry that gives a brief description of what it is abstracting, its derivation, and to which group it belongs: abstract type (a type that adds functionality or restricts domain; it does not specify an implementation and you cannot make an object of that type), implementation type (a class that implements its abstract parent; you can make an object of that type), abstract implementation type (defines new functionality that implies a particular implementation, or this implementation of the parent may imply added functionality; you can make an object of that type), or leaf implementation type (implementation type that is defined to bypass the virtual mechanism for efficiency when that type is used explicitly; it is usually not meaningful to derive a type from a leaf implementation type). The static and member operations defined for each type are also listed and described.
The typical abstract data type class definition:
class newType
:public baseType
{protected:
// data members
// virtual members
public:
// interface functions
};
Note that this definition follows the design rule: never make data members and virtual functions public, rather provide interface functions to access them indirectly instead. This rule allows derived classes to more easily override the base class definitions. Given the definition above, the separation into OATH accessor and internal classes is straight-forward:
CLASS_G(newType, baseType)
// data members
// virtual members
};
CLASS_A(newType, baseType)
public:
// interface functions
};
Following the class definitions, the inline functions need to be defined. A cpp macro INLINES(newType, baseType) will define all the standard inline functions which were declared in the class macros.
Typically in a separate source file, the outline functions and the static data members will be defined. The standard of those will be defined by the cpp macro OUTLINES(newType, baseType).
A common function signature in OATH types returns the object itself to allow concatenation of commands, such as L.insert(O).insert(P).insert(Q) or L << O << P << Q. These functions must be redefined by each derived class so that they return the appropriate type. To facilitate this, each type defines a macro extension of its parents macro which defines these functions. These macros are typically named <typeName>_RETURNING_DEFINTIONS(newType) and <typeName>_RETURNING_INLINES(newType).
Note that a const listA is a constant accessor, not a constant list. Without const and non-const constructors in C++, it is not possible to make this mean a constant list. Thus, all const-checking in OATH is performed at runtime. A non-const member function on an internal class should call macro NOT_CONST() to verify that it is legal to modify this object.
To help OATH users understand our naming conventions, we would like to share our rationale. We could have chosen to be consistent with: 1) our native language (English), 2) our programming language (C++), or 3) other libraries that we will use (X, InterViews). We feel it is most important to be consistent with C++ and then with English, since these are the two languages that we use most often and are most comfortable with. Consistency with other libraries is secondary.
Fortunately, the conventions in C++ and English are relatively consistent. For instance, in English you would say "dog Rover" not "Dog rover", or "singer Elvis" not "Singer elvis". In C++ you would say "int I" not "Int i", or "ofstream OutFile" not "Ofstream outfile". In English you would say "Rover, roll over" not "rover, Roll Over" or "Elvis, sing a song" not "elvis, Sing a Song". In C++ you would say "abs(I)" not "Abs(i)", or "OutFile.put('c')" not "outfile.Put('c')". In both cases, type names (common nouns) and function names (verbs) are *not* capitalized. In English, variable names (proper nouns) are always capitalized.
This is the foundation for our conventions. Unfortunately, it is not consistent with many existing C++ libraries (like InterViews) whose conventions were adopted from the related C conventions for struct names and the associated typedef names. This is unfortunate.
Our conventions:
All #define and enum constants and #define macros are written ALL_CAPS.
All variable names are written EachWordCapitalized.
All function and class names are written firstWordLowercase.
Class names are nouns (or noun phrases). Accessors end with a capital 'A'. Internal classes end with a capital 'G'. Private auxiliary classes end with a capital 'P'.
Non-member function names are verbs (or verb phrases).
Member function names:
Functions that return true/false are named with a being or passive verb. (e.g. isEqual, contains, canMatch)
Functions that manipulate or transform the object are named with an action verb (e.g. insert, extract, increment).
Member functions that alter a parameter are suffixed with an X (e.g. Bag1->applyX(Fn, Bag2) alters Bag2).
Functions that access members are named the same as the member except lowercase. To set the member you pass the new value; to get the member you pass nothing:
float UpperWidth;
float upperWidth ();
void upperWidth (float);
It is not necessary to provide both set and get accessors.
OATH is largely experimental software and is relatively untested. If you find bugs or deficiencies or identify opportunities for improvement, please let us know via email oath@csc.ti.com. All messages sent to this address will be read; however, responses will be limited by our available time (OATH is not a product -- so nobody is assigned to supporting it).
If you write new, generally useful OATH types or improve the existing types and are willing to contribute your code to the OATH distribution from TI, please submit your code via email to oath@csc.ti.com.
B. M. Kennedy, "The Features of the Object-oriented Abstract Type Hierarchy (OATH)", Proc. USENIX C++ Conference, 1991, pp 41-50. (Available in the Postscript file doc/FeaturesOfOATH.ps in this release.)
Copyright (C) 1991, 1990 Texas Instruments Incorporated
Permission is granted to any individual or institution to use, copy, modify, and distribute this software, provided that this complete copyright and permission notice is maintained, intact, in all copies and supporting documentation. Texas Instruments Incorporated provides this software "as is" without express or implied warranty.
Permission is granted to any individual or institution to use, copy, modify, and distribute this software, provided that this complete copyright and permission notice is maintained, intact, in all copies and supporting documentation. Texas Instruments Incorporated provides this software "as is" without express or implied warranty.
Some of the functionality has not been fully implemented because it hasn't been needed in our use of OATH. Similarly, much of the functionality is untested. Do not assume the OATH classes are bug-free.
OATH has only been compiled with ATT cfront 2.1 compatible compilers. There is one warning that is commonly emitted and should be ignored: "warning: statement not reached" (which is actually emitted by the C compiler.) This causes no problems.
To compile with ATT cfront 2.0 based compilers you will have to modify the source code of OATH. At a minimum, you will need to replicate all inherited pure virtual functions or make them non-pure (2.0 did not allow pure virtual functions to be inherited).
Access control has not been exercised much! The intent is that code outside OATH classes should not use the internal classes (the "G" classes) at all. Such code should only use the accessor classes and should never use the function guts(), which is currently public. Once C++ compilers are available that correctly handle nested classes and the associated access control, we intend to restrict access to the internal classes to OATH class members. In other words, when using OATH you should only use accessor classes (those ending in 'A') and their interface functions, except for function guts() which you should not use. When deriving new OATH types, you will need to use guts() and the internal 'G' classes.
C++ does not really provide adequate support for smart pointers, accessors, or any of the variations -- no matter how you do it, there are potential problems. For more on this issue, see "Smart Pointers: They're Smart, but They're Not Pointers" by Daniel R. Edelson in the Proceedings of the 1992 USENIX C++ Conference. In particular, OATH accessors are not completely type-safe since it is possible to use a pointer or reference to an accessor incorrectly without compiler warning. For example,
void assign (objA& A, objA B) { a = b; }
void foo ()
{characterA C = characterA::make('A');
stringA S = minStringA::make();
assign(S, C); // wrong!! but allowed!!
}
The call to assign is legal, S and C are objA's, however the resulting computation is clearly bogus -- you cannot have a stringA accessing the same object as a characterA. Unfortunately, we cannot tell C++ to disallow such conversions. The only option is to remove the assignment operator for accessors, making them SmartReferences. However, they would greatly strain expressibility. For purposes of experimentation (this is an experimental library designed to explore greater reusability), the assignment operator remains.
If you are building on OATH, you can: 1) ignore (or remove) the assignment operator and make related changes; 2) avoid use of non-const pointers or references to accessors; 3) just keep this potential problem in mind when tracking down a bug (my current approach).