17.2.4 The Type-Aware Base Class taBase

There is a basic class type called taBase that uses the TypeAccess type information to perform a number of special functions automatically. This object is aware of its own type information, and can thus save and load itself, etc. Special code has been written in both the TypeAccess system and in CSS that takes advantage of the interface provided by the taBase type. Thus, it is recommended that user's derive all of their types from this base type, and use special macros to provide derived types with the hooks necessary to get their own type information and use it effectively. The type TAPtr is a typedef for a pointer to a taBase object. The definition of a taBase object and the macros that are used with it are all in `ta/ta_base.h'.

All taBase objects have only one member, which is a reference counter. This provides a mechanism for determining when it is safe to delete an object when the object is being referenced or pointed to in various different places. taBase provides a set of referencing and pointer-management functions that simplify the use of a reference-count based memory management system. Ref increments the reference count, unRef decrements it, Done checks if the refcount is zero, and deletes the object if it is, and unRefDone does both. Own both Ref's an object and sets its owner. For pointers, SetPointer unrefs any existing object that the pointer points to, and sets it to point to the new object. DelPointer does an unRefDone on the object pointed to, and sets the pointer to NULL. OwnPointer is like SetPointer except it also sets the owner of the pointed-to object to the one given by the argument. See section 17.8.1 Basic Functions and `ta/ta_base.h' for more details.

The one essential function that taBase provides is GetTypeDef(), which is a virtual function that returns a pointer to the TypeDef type descriptor for this object. This function is defined as part of the basic macro TA_BASEFUNS, which must be included in all classes derived from taBase. This function makes it possible for a generic pointer to a taBase object to find out what type of object is really being pointed to.

There are a number of functions defined on the taBase type that simply call the corresponding function on the TypeDef pointer. These can be found in the `ta/ta_base.h' header file. They just make it easier to call these commonly-used functions, instead of requiring the user to put in a GetTypeDef function in between.

taBase also provides a simplified way of managing the construction, deletion, and copying of an object. Basically, construction is broken down into a set of functions that Initialize the member variables, Register the new token with the type if it is keeping track of tokens, and it sets the default name of the object based on its type name using SetDefaultName. The TA_BASEFUNS macro defines a default constructor that calls these three functions in that order. The user thus needs to provide a Initialize function for every class defined, which does the appropriate member initialization. Note that if this function is not defined, the one on the parent class will be called twice, so its more efficient to include a blank Initialize function when there are no members that need to be initialized.

The destructor function is similar to the constructor. A default destructor is defined in TA_BASEFUNS, which simply calls unRegister, and Destroy. Thus, the user needs to provide a Destroy function which frees any additional resources allocated by the object, etc. Like Initialize, a blank Destroy should be defined when there is nothing that needs to be done to prevent the parent function from being called twice.

Copying, cloning, and making a new token of the given type are also supported in the taBase class. The Copy function performs the basic copy operations for both the copy constructor and the = operator. This should replace the values of this class and any of its existing sub-objects with those of the object passed to it, as it is intended for assignment between two existing objects. In general, the = operator should be used for copying all members, except for the case of LINK_GROUP groups and lists, which should use the BorrowUnique function (since they do not own the items in the list, just link them). Copy must call the parent's Copy function as well. As a minor simplification of calling the parent (and to provide a copy function for just the items in a given class), it is conventional to define a Copy_ function, which does everything except for calling the parent copy function. The macro COPY_FUNS can be used to define a Copy function which calls the parent function and then Copy_. The macro SIMPLE_COPY defines a Copy_ function which uses the type-scanned information to do the copying. It is slower than hand-coding things, so it probably shouldn't be used on types which will have a lot of tokens or be copied often.

A Clone function which returns a TAPtr to a new duplicate of this object is defined in TA_BASEFUNS, as well as an "unsafe" version of Copy (UnSafeCopy), which takes a generic TAPtr argument and casts it into their type. The argument's type should thus be tested before calling this function. A safe interface to this function is provided by the CopyFrom function, which does the type checking. Finally, the MakeToken function will create a new token of the type.

The taBase class also contains functions for creating and manipulating a structure hierarchy of objects. This is where certain objects contain groups of other objects, which contain other objects, etc. For example, the PDP++ software has a structure hierarchy built around a root object, which contains projects, which contain lots of other objects like networks, projects, environments, etc. Special container objects like taList and taGroup play an important role in representing and manipulating this structure (note that it is possible to write other types of container objects which could play the same role simply by overloading the same functions that these objects do).

When an object is "linked" into the object hierarchy, a function called InitLinks is called. This function should perform any kind of initialization that depends on the object being situated in the hierarchy, like being able to know what object "owns" this one. taBase has functions for getting and setting the owner of an object. For example, when a group (taList or taGroup creates a new object and links it into its list of objects, it calls the SetOwner function with a pointer to itself on this new object, and then it calls InitLinks. Similarly, when the object is removed from the group, the CutLinks function is called, which should cut any links that the object has with other objects.

An object's location in the object hierarchy can be represented by a path to that object from a global root object. A given application is assumed to have a root object, which contains all other objects. A pointer to that object is kept in tabMisc::root, which is used to anchor the path to any given object. An object can find its path with the GetPath function, and an object can be found from a path with the FindFromPath function.

Finally, a function for allowing an object to set the values of certain members based on changes that might have been made in other members after a user edits the object, called UpdateAfterEdit, is provided. This function is called on most objects after they are loaded from a save file (except those with the #NO_UPDATE_AFTER comment directive), and on all objects after the user hits Apply or Ok in an edit dialog, and after any member is set through a CSS assign statement. While the object does not know which members were changed when UpdateAfterEdit is called, the object can buffer previous state on its own to figure this out if it is needed.

For a step-by-step guide to making a new class that derives from taBase, see section 17.4 Coding Conventions and Standards.