\begindata{text,538527048} \textdsversion{12} \template{default} \define{global } \define{programexample } \define{itemize } \define{enumerate } \chapter{Programming Environment} This document describes the Andrew Toolkit programming environment, including its conventions for modules, method definition and use, the Andrew class mechanism, and debugging techniques. Parts of this document are incomplete -- there are no short-term plans for completing this document, however. \section{Modules and Header Files}\indexi{Header files} You are probably already familiar with the C convention for creating a module: when you create a module, you locate all the exported data types and procedures in a \italic{header} or \italic{.h} file; then anyone who wants to use the module can import it cleanly and easily by a \italic{#include} of the \italic{.h} file. Also by convention, the code for the module goes in a corresponding \italic{.c} file. For example, if you were creating a module for a data type named \italic{stack}, you would put all the external data structures and procedures for the \italic{stack} in a header file named \italic{stack.h} and the code for the module would go in the file \italic{stack.c}. \ The class language that the Andrew Toolkit is built upon follows a similar convention for classes. For each class you are creating, you should declare the class in a \italic{.ch} file with the same name as the class. For example, if you are creating a class named \italic{stack}, you should declare it in a file named \italic{stack.ch}. Note that the file extension is \italic{ch}, not \italic{h}. In addition to the class definition itself, you can include any other statements that you might normally put in a conventional C \italic{.h} file for a module. Likewise, the code for the class methods and procedures goes in the file \italic{stack.c}. \ There is a class preprocessor, \italic{class}, that takes a \italic{.ch} file as input and generates two conventional C \italic{header} files as output: a \italic{.ih, }or \italic{import header} file, and a \italic{.eh,} or \italic{export header} file. For example, if you were creating a class named \italic{stack} and had declared it in \italic{stack.H}, then the following command: \ \bold{\leftindent{class stack.H}} produces \italic{stack.ih} and \italic{stack.eh.} \ The class preprocessor converts the class definitions written to a series of C \italic{structs} and \italic{#define} statements suitable for the C preprocessor. The \italic{.eh} file contains the same information as the \italic{.ih} file and in addition, contains information that allows the module to access the methods of the class' superclass. In general, you do not need to know details about what the class preprocessor generates; the exception is during debugging. \ \subsection{Imports}\indexi{Imports} Anyone who wants to use a class can import it by a \italic{#include} of the \italic{.ih} file. For example, anyone who wanted to use the class \italic{stack} could import it by a \italic{#include stack.ih} at the start of the module in which it is to be used. \ \subsection{Exports}\indexi{Exports} In the module defining the class methods and procedures, you must (1) include \italic{class.h} and (2) export the new class by a \italic{#include} of the \italic{.eh} file. For example, in the \italic{stack.c} file defining the class \italic{stack}, you must (1) \italic{#include} \italic{class.h} as an import and (2) export the new class \italic{stack} by a \italic{#include} the \italic{stack.eh} file. \ \subsection{Dynamic loading}\indexi{Dynamic loading} Classes in ATK are usually loaded dynamically. When ATK encounters an unloaded class, it searches for the corresponding files along a dynamic class path \smaller{CLASSPATH}., taken from the user's \italic{.cshrc} file. The following is the recommended class path for inclusion in a \italic{.cshrc}.: \ \programexample{ if (! $?SYS) setenv SYS `/usr/andrew/bin/sys` setenv CLASSPATH .:"$SYS":$HOME/lib/do/"$SYS":/usr/andrew/dlib/atk } Or, if you are running the Andrew Toolkit on top of the X11 window manager, type the following from your Xterm window: \ \programexample{ setenv BE2WM x11 } With this class path, the Andrew Toolkit dynamic object search will look in the \italic{current directory}, in \italic{/usr/andrew/bin/sys}, in the user's \italic{home directory} under \italic{lib/do} and in \italic{/usr/andrew/dlib/atk}. \ If you do not have a class path defined in your \italic{.cshrc}, you will probably want to define one. The class path defaults to \italic{/usr/andrew/dlib/atk}. \indexi{Class path++Default} To see whether a given class is on your class path, \indexi{Class path} in the \bold{Typescript }(or other command window), type: \ \leftindent{\bold{whichdo \italic{<your class name>}}} The command \italic{whichdo} \indexi{ \italic{whichdo}} (\italic{which} \italic{d}ynamic \italic{o}bject) is analogous to the \italic{Unix 4.2 BSD} command \italic{which} (see \bold{help which} for more information); it takes a list of class names and looks for the files which the Andrew Toolkit would dynamically load in response to the names. Each argument is expanded if it is aliased, and searched for along the dynamic class path, \smaller{CLASSPATH}. \ \section{Defining versus using methods} \indexi{Defining methods} \indexi{Using methods} \indexi{Methods} Note that when you \italic{define} class procedures or methods, you must use a double underscore; when you \italic{use}, or call, the procedures or methods, you only use a single underscore. For example, if you are creating the class \italic{stackview} and defining a \italic{FullUpdate} method, you use the double underscore, and write \italic{stackview__FullUpdate} as above. If you were to call stackview's \italic{FullUpdate} method, you would use a single underscore, i.e., \italic{stackview_FullUpdate}. If you look in the source code for the Toolkit classes (see /usr/andrew/src/atk), all the class procedures and methods are defined with the double underscore, although when you, the application programmer, call the class procedures or methods, you only type one underscore. \ \section{Calling conventions for superclass methods}\indexi{Superclass} A class inherits methods from its superclasses. But when you use a method defined in a superclass class, you should call it with the class name of its first actual parameter, not the class name of the method itself. This is because an intervening class may have redefined the method or may redefine the method in the future. For example, you call \italic{stackview_GetVisualBounds}, not \italic{graphic_GetVisualBounds}, since the first parameter to the method is a view, even though the method \italic{GetVisualBounds} is defined in \italic{graphic}. \ In general, for any object \italic{x} in \italic{classx}, (\italic{i.e.}, \italic{struct classx *x)}, you call \italic{classx_methodname (x, <other parameters>)} in order to call either a method for object x or a method for any of \italic{x}'s superclasses. Thus, for object \italic{stck} in the class \italic{stackview}, you write \italic{stackview_GetVisualBounds (stck, <other parameters>)} to call the graphic method \italic{GetVisualBounds}. \ \section{The Class system} \indexi{Class system} This section describes the Andrew Class system (to be referred to as Class) that has been used to build Andrew Toolkit. Class provides an object-oriented environment for building programs using the C programming language. Using a simple preprocessor which is run only on header files, the Andrew Class system allows you to create classes using single inheritance model. Class also provides a mechanism for dynamic linking of code into a running C program. \ \subsection{Encapsulation of Data and Operations} \indexi{Data encapsulation} Object-oriented programming provides a facility to encapsulate both data and operations on that data into a single unit. In plain C, you define a structure which contains data and then define a set of routines that operate on instances of that structure. The data and routines are only connected because one of the parameters to the routine is an instance of the structure. In an object-oriented model, you define a class which is the combination of both data and methods (the routines associated with the class). The data and methods are tightly coupled, in that you need to have an instance of a class to be able to call one of its methods. The instance of a class actually contains a pointer to the set of methods that operate on the data. \ For example, the following structure, using normal C, defines a structure that can be used to build a simple family tree: \ \example{ struct person \{ struct person *mother; struct person *father; struct person *sibling; struct person *children; \};} \ and a routine that determines if y is an ancestor of x: \ \example{ boolean IsAncestor(x, y) struct person *x; struct person *y; \{ if (x->mother == y || x->father == y) return TRUE; if (x->mother != NULL && IsAncestor(x->mother, y)) return TRUE; if (x->father != NULL && IsAncestor(x->father, y)) return TRUE; return FALSE; \}} This routine needs to be called with pointers to two instances of the person structure, but it could be called with any random memory locations. The use of the type boolean is a convention that we have used to provide more type information than is allowed using normal C types. \ Using Class the same definition is written as follows: \ \example{ class person \{ methods: IsAncestor(struct thisobject *y) returns boolean; data: struct person *mother; struct person *father; struct person *sibling; struct person *children; \}; } and the method definition: \ \example{ boolean person__IsAncestor(self, y) struct person *self; struct person *y; \{ if (self->mother == y || self->father == y) return TRUE; if (self->mother != NULL && person_IsAncestor(self->mother, y)) return TRUE; if (self->father != NULL && person_IsAncestor(self->father, y)) return TRUE; return FALSE; \}} The class definition contains the same data as the example structure definition but also includes the list of methods that operate on an instance of the class. In this example there is only one method (there would normally be more for a full implementation of a family tree). Note that the declaration of the method only contains one parameter. The initial parameter, is automatically assumed since you will need to have that parameter in order to call the method. The type of the second parameter utilizes a special keyword, thisobject, which is replaced by the type of the class. This is used for handling type-checking when inheriting methods (described below). \ The method definition also is similar to the earlier example except the name of the actual method (person__IsAncestor) is different than the call to the method (person_IsAncestor). The name of the actual method contains two underscores separating the class type and the method name while the call to the method contains only a single underscore. The call to the method is actually an indirect call via the list of methods attached to an instance of the class. If you tried calling person_IsAncestor without an instance of the class person, the program would not call person__IsAncestor since that parameter would not contain the list of methods pointing to person__IsAncestor. \ \subsection{Inheritance} \indexi{Class inheritance} \indexi{Superclass} \indexi{Subclass} \indexi{Inheritance} Inheritance provides a mechanism for building complex objects out of simpler objects. Class provides single-level inheritance. In this case, a class can be defined to be a subclass of one other class. When a class is a subclass of another class (its superclass) it inherits both the data and methods associated with its superclass. The subclass can override any of the methods associated with the superclass. If the subclass does not override a method the superclass method provides is used. In other words the superclass provides a set of default methods that are used by the subclass. Further because a subclass inherits both the data and the methods associated with its superclass, an instance of the subclass can be used wherever an instance of the superclass can be used. \ The following class definition provides a simple list of elements: \ \example{ class list \{ methods: NumberOfElements() returns long; NextElement() returns struct thisobject *; PreviousElement() returns struct thisobject *; AddElement(struct thisobject *newElement); RemoveElement(); data: struct list *next; struct list *head; \};} \ This class contains these methods: \ \description{ NumberOfElements -the list. \ NextElement -- returns the element following an entry in the list. \ PreviousElement AddElement -- RemoveElement } and returns the number of elements following an entry in -- returns the element preceding an entry in the list. Adds in an entry into the list. \ -- Removes an entry from the list. \ two data elements: \ \description{ next -- a pointer to the next entry in the list. \ head -- a pointer to the head of the list. This is necessary for being able to both find the previous entry in the list as well as being able to delete elements from the list. }\ Now let us define the class dlist (a doubly-linked list) that is a subclass of list: \ \example{ class dlist : list \{ overrides: PreviousElement() returns struct thisobject *; AddElement(struct thisobject *newElement); RemoveElement(); data: struct dlist *prev; \};} \ The beginning of the class definition declares dlist to be a subclass of list. This class contains the same set of methods as list but has chosen to override three of the methods. The PreviousElement method for list must start at the beginning of the list in order find the element preceding the given entry while the PreviousElement method for dlist need only look at the prev data entry. The same is true for the RemoveElement method. The AddElement must also be overridden so that the prev field for dlist is filled in. The other two methods need not be overridden since they can still be computed in the same fashion. \ Now let us examine actual implementations of the list and dlist AddElement methods: \ \example{ void list__AddElement(self, newElement) struct list *self; struct list *newElement; \{ newElement->next = self->next; newElement->head = self->head; self->next = newElement; \} void dlist__AddElement(self, newElement) struct dlist *self; struct dlist *newElement; \{ newElement->prev = self; super_AddElement(self, newElement); \}} The dlist method first sets the prev field for the newElement and then invokes the AddElement method for its superclass (the call super_AddElement) in order to complete the operation. In this case it will call list__AddElement. This ability for a method to invoke the method associated with its superclass allows you to build methods that add to the code provided by the superclass without replicating that code. \ Now examine implementations of the list and dlist RemoveElement methods: \ \example{ void list__RemoveElement(self) struct list *self; \{ struct list prevElement; prevElement = list_PreviousElement(self); if (prevElement != NULL) prevElement->next = self->next; else \{ /* Deleting head of the list, adjust head field in the remainder of the list. */ for (nextPtr = self->next; nextPtr != NULL; nextPtr = nextPtr->next) nextPtr->head = self->next; \} self->next = NULL; self->head = self; \} void dlist__RemoveElement(self) struct dlist *self; \{ if (self->next != NULL) self->next->prev = self->prev; super_RemoveElement(self); \}} As with the earlier example the dlist method calls the list method to do most of the work required to remove an element from the list. In the subsequent invocation of the PreviousElement method the dlist__PreviousElement routine is called and not the list__PreviousElement routine. Method invocations are not calls directly to a routine but is a call to the routine associated with the object. Since dlist overrode the PreviousElement method when list_PreviousElement is called on an instance of a dlist the dlist__PreviousElement routine is called. \ So far our example does not allow us to have any data associated with either of the two types of lists. We can now provide a new definition of the person class that builds upon the dlist class for maintaining the list of siblings: \ \example{ class person : dlist \{ methods: IsAncestor(struct thisobject *y) returns boolean; data: struct person *mother; struct person *father; struct person *children; \};} \ Person inherits all the list manipulation methods provided by dlist and adds on the IsAncestor method. \ \subsection{Modules} \indexi{Modules} Due to the dynamic loading feature of the Class system classes are implemented by creating a module per class. The module contains all the code associated with the class and any data associated with the class. Direct references between modules are not permitted. The Class preprocessor provides ways to call the code without containing direct references. References to data contained in other modules are not allowed. In this way a module is a self contained unit that can quickly be dynamically loaded into a running program. \ A module can be created by using separately compiled C files and interfile reference to data may occur between those files. \ \subsection{Creating an Instance of a Class} \indexi{Creating a class}\indexi{Class++Creating} The Class preprocessor automatically creates a number of procedures. Two of these procedures are New and Initialize. To create an instance of a normal C structure a call is either made to malloc with the size of the structure passed in as its parameter, the structure is statically allocated, or the structure automatically allocated on the stack. These methods do not suffice for creating instances of a class since they can not properly associate the methods with the data. \ The New procedure is used as the substitute for the call to malloc. To create an instance of the person class a call to person_New() is made. Unlike the call to malloc this call also initializes the data associated with the class (via a procedure person__InitializeObject that you provide). The choice to return initialized data was made in an attempt to reduce the number of uninitialized data errors that occurred. The New procedure is the preferred way to create an instance of an object. \ There is no direct substitute for allocating an instance of the structure either statically or automatically on the stack. In order to do that you must first allocate the abject in that same way as in normal C (struct person anyone) and then call the Initialize procedure associated with the class (person_Initialize(&anyone)). This will both associate the methods with the data and initialize the data. \ In the case of a statically allocated instance of a class, it must be initialized before it is used. This is more difficult to do than the case where the instance is automatically allocated on the stack. In the latter case the first statement following the declarations can be a call to Initialize. A way to handle this case will be described later when we discuss initializing the entire class. \ \subsection{Initializing the Data of an Instance of a Class}\indexi{Class++Initializing data} As stated above the New and Initialize procedures call the InitializeObject procedure. This is a procedure that you provide. It is a required procedure (except in the case where the class has no data associated with it). Whenever an instance of a class is created, the data associated with its superclass is initialized followed by a call to InitializeObject for the class to initialize that data directly associated with the class. \ For example, during a call to person_New, a call would first be made to list_InitializeObject, then to dlist_InitializeObject and finally to person_InitializeObject. \ \subsection{Deleting an Instance of a Class}\indexi{Class++Deleting} Two more procedures created by the Class preprocessor are used for deleting an instance of a class. These two procedures are Destroy and Finalize. Destroy is used when deleting an instance of a class that has been created using the New procedure. Finalize is used on either an instance of a class that has either been statically allocated (and you are no longer going to use it) or an instance of a class that has been automatically allocated on the stack. In the latter case it must be called prior to leaving the block in which the instance was allocated. \ Any data that is pointed to by the instance of the class that is being deleted may be deleted in the process of deleting the instance of the class. You can determine which data will be deleted by providing a FinalizeObject procedure. This procedure will normally undo the operations done by the InitializeObject procedure. \ \subsection{Class Procedures}\indexi{Class procedures} In the previous section we have been describing a set of procedures that are not methods associated with an instance of a class. These procedures are considered to be acting on the class itself, thus the name class procedures. These are almost equivalent to procedures from normal C. The difference is due to the desire to dynamic load code within the Class system. \ Class procedures differ from methods in several ways. A class procedure can be called without having an instance of the class. A class procedure may require an instance of a class as one of its parameters (just as a procedure in normal C will take an instance of a structure). The New procedure can not possibly be called with an instance of a class. Class procedures are not inherited by a subclasses. Class procedures can not be overridden by a subclass. Finally, the actual definition of the class procedure requires an initial dummy parameter (again due to the dynamic loading nature of Class). \ Other than class procedures used by the Class system, you should rarely need to define your own class procedures. One exception to this arises if you want to have a procedure that creates an instance of an object with a set of initial values provided as parameters to the procedure. Another exception is when the class is acting as a database of all instances of the class. \ The following extends the person example from above: \ \example{ class person : dlist \{ methods: IsAncestor(struct thisobject *y) returns boolean; classprocedures: Create(char *name, struct person *mother, struct person *father) returns struct person *; Lookup(char *name) returns struct person *; data: struct person *mother; struct person *father; struct person *children; char *name; \};} \ The Create procedure creates a new instance of person with the name, mother and father fields initialized appropriately. It will also add the name and a pointer to the instance into a database being maintained by the the class. The Lookup procedure will look in that database and locate the instance associated with the name provided. \ A call to a class procedure looks like a normal C call except that the class name is prepended in front of the procedure name, separated by a single underscore (i.e. person_Locate("Andrew J. Palay")). The definition of the class procedure is slightly different as it takes an initial dummy parameter. The Class preprocessor adds in that parameter automatically. Thus the definition of Locate looks like: \ \example{ struct person *person__Locate(classID, name) struct classheader *classID; char *name; \{ /* The code for Locate */ \}} As with methods the actual definition has a name separated by two underscores,while the call has only a single underscore. \ Class procedures such as InitializeObject and FinalizeObject are written in the same fashion, although they are never called directly only as a result of calling the class procedures provided by Class (New, Initialize, Destroy and Finalize). Thus the InitializeObject procedure for list would look like: \ \example{ void list__InitializeObject(classID, self) struct classheader *classID; struct list *self; \{ self->next = NULL; self->head = self; \}} \subsection{Initializing the Class}\indexi{Class++Initializing} There is often data associated with a class. The database described above is one such example. Another example is when all instances of a class want to share some piece of data that needs to be created or initialized (i.e. an instance of another class). To handle this case, you can include an InitializeClass declaration as one of the class procedures in the class declaration. The InitializeClass procedure will be called before any other class procedure or method for the class is called. \ The InitializeClass procedure is used to initialize an instance of a another class that has been allocated statically within the module associated with the class that is being initialized. The InitializeClass procedure would call Initialize on the instance of the other class. Another way to handle this same case is to only statically allocate a pointer to the other class and in the InitializeClass procedure call the New procedure for the other class. \ \section{How to Use the Class System}\indexi{Class++Using} Class has been designed to be a very simple environment that supports object-oriented programming with single-level inheritance. It has been designed around a preprocessor that only processes header files (normally with the extension of .H). A class header file looks very much like a normal C header file except that it will contain a class (or possible a package (described below)) declaration. When the preprocessor runs it generates two files: an .ih file which is used when you want to import (use) a class in another module and an .eh file that is used to export the class to the outside world. The .eh file is included in the module that is defining the class. There can only be one class declaration within a .H file and only one class defined within a module. \ In processing the .H file the preprocessor directly copies all the contents of the .H file except for the class declaration. It uses that declaration to create the appropriate structure for class and a large set of macros that are used for calling both the methods and class procedures associated with the class. It also generates code, that is placed into the .eh file, defining the New, Initialize, Destroy and Finalize class procedures as well as several procedures required by the Class run time system. The remainder of this section describes the format of the class declaration and how to use an instance of a class in a program. \ \subsection{Defining and Using a Simple Class}\indexi{Class++Defining} The list class definition presented earlier is an example of how to define a simple class (a class that is not a subclass of another class). Again the definition is: \ \example{ class list \{ methods: NumberOfElements() returns long; NextElement() returns struct thisobject *; PreviousElement() returns struct thisobject *; AddElement(struct thisobject *newElement); RemoveElement(); data: struct list *next; struct list *head; \};} \ Class in a reserved word to the preprocessor. It signifies what follows will be a well formed class definition. Immediately following the word class is the name of the class (in this case list). If that is followed by a colon it states that the class is a subclass of another class. For a simple class the name will be followed by an open brace. After the open brace comes a set of categories: methods, macromethods, classprocedures, macros. These may occur in any order but we have normally used the above order. Following that set of categories comes the data category. The data category is terminated by the closing brace. The category marker is the name of the category followed by a colon. \ \paragraph{Methods}\indexi{Methods} Method declarations take the form of the name of the method followed by the list of parameters, separated by commas, to the method within parentheses. This is followed by an optional returns element and the type that the method returns. You may declare a method to return a pointer to a procedure however you will need to either just say "returns procedure" where procedure is a typedef to a pointer to a procedure that returns an integer or create your own typedef for a pointer to a procedure that returns the appropriate type. A method that does not have a returns statement will be declared to be of type void. Finally the declaration is terminated by a semi-colon. \ The parameter list associated with a method contains optional type information. The type information is currently ignored, but has been made available as a quick reference and with the hope that we can later provide a system that does more type checking than currently available. The parameter list is required to have the correct number of parameters and that the parameters have unique names. \ Thisobject is a special keyword that is used in the type information. When the preprocessor sees that type it substitutes the name of the class for thisobject. Thisobject has been provided to handle subclassing. In the earlier list/dlist example the NextElement method will return a pointer to an instance of a list when handed a pointer to an instance of a list and will return a pointer to an instance of a dlist when handed a pointer to an instance of a dlist. \ To call a method you must first have an instance of the class. The following code cycles though all the elements of a list headed initially by the element ptr: \ \example{ while (ptr != NULL) ptr = list_NextElement(ptr);} \ Calls to methods always have as their first parameter a pointer to the instance of the class that is to be acted upon and then the other parameters listed in the declaration. The type of the first parameter is also added prior to the name of the method, separated by a single underscore. \ Since you need to have pointer to an instance of a class to call one of its methods all tests to see whether the pointer is NULL must be done prior to the method invocation. Having the first line of a method test to see if the first parameter is NULL is useless since in order to call the method the parameter could not have been NULL. \ To actually define a macro, you write what appears to be a normal procedure declaration except that the name of the procedure is the name of the class followed by two underscores and the method name. \ \paragraph{Macromethods}\indexi{Macromethods} Macromethods are an attempt to combine to concept of macros and methods. Just as it is often the case that it is more reasonable to use macros instead of routines in normal C, it is sometimes more reasonable to use macromethods instead of methods when using Class. \ A macromethod declaration begins just like a method declaration through its list of parameters. At that point the macro definition of the code begins. The macro definition is written in the same fashion as a define using the C preprocessor, with one exception. There will be one additional parameter to the actual macromethod call that being "self". Just as there is one additional parameter to methods there is also an additional initial parameter to a macromethod call. \ In the list example the NextElement method could have been declared to be a macromethod in which case the list declaration would have looked like: \ \example{ class list \{ methods: NumberOfElements() returns long; PreviousElement() returns struct thisobject *; AddElement(struct thisobject *newElement); RemoveElement(); macromethods: NextElement() (self->next) data: struct list *next; struct list *head; \};} \ A call to a macromethod looks exactly like a method, but since it is indeed a macro it is necessary to be careful about using parameters that have side effects (i.e. i++), since that parameter might be instantiated multiple times during the expansion of the macromethod. \ \paragraph{Classprocedures} The class procedure category contains a similar list of declarations to the methods category. The form of the declaration of a class procedure is identical to the form for a method: the class procedure name, followed by the list of parameters, optionally followed by a returns section and terminated by a semi-colon. Class procedures are also called in a similar fashion as methods: the name of the class, underscore, the name of the class procedure and then the list of parameters. Unlike a method there is no additional initial parameter to the class procedure. \ As stated earlier the definition of the class procedure looks like a normal C procedure except that it contains a dummy first parameter. \ There are a set of special class procedures that are used by the Class system. As stated earlier the Class preprocessor creates four special class procedures: New, Initialize, Destroy and Finalize. Class also requires you to provide an InitializeObject class procedure. If you include a FinalizeObject class procedure, it will be called whenever an instance of the object is Destroyed. If you provide an InitializeClass procedure it will be called prior to any call to another class procedure or method for that class. Finally, you can provide Allocate and Deallocate class procedures. Normally when the New procedure is called it calls malloc to get the storage for the new instance of the class. When the Destroy procedure is called, the storage is normally returned via a call to free. If you provide Allocate and Deallocate procedures then New will call Allocate to get storage and Destroy will call Deallocate to return the storage. \ \paragraph{Macros} \indexi{Macros} Macros are equivalent to classprocedures that are expanded inline in the code. They are defined in the same fashion as macromethods except there is no extra parameter self to be used in the actual definition of the macro. The invocation of a macro is identical to the invocation of a classprocedure. As with macromethods, you must be careful not to pass parameters with side effects to a macro. \ \paragraph{Data}\indexi{Data} When processing a simple class, the preprocessor creates a structure that contains the fields provided in the data category along with an initial first entry that points to the set of methods associated with the class. Accessing an field in the class is done in the identical fashion as a normal C structure. \ For example the structure created for the class list is: \ \example{ struct list \{ struct \{ struct basicobject_methods list_methods; \} header; struct list *next; struct list *head; \};} \ \subheading{Defining and Using a SubClass}\indexi{Subclass} The dlist example presented earlier shows how to define a subclass. The two additional categories that are added in this case are overrides and macrooverrides. \ \paragraph{Overrides}\indexi{Overrides} The overrides declarations are identical in form to the method declarations. The separation between overrides and methods has only been made to catch typing errors. If you override a method that does not exist in the superclass, the preprocessor will generate an error. Similarly, if you declare a method that exists in the superclass, the preprocessor will again generate an error message. \ \paragraph{Macrooverrides}\indexi{Macro overrides} The macrooverrides section is used to override macromethods. They are expressed in the same form as macromethods. This category has been provided to catch typing errors. \ \paragraph{Data}\indexi{Data} When processing a subclass, the preprocessor creates a structure that contains the fields provided in the data category appended to a structure containing the data associated with the superclass. \ For example the structure created for the class dlist is: \ \example{ struct dlist \{ union \{ struct basicobject_methods dlist_methods; struct list list; \} header; struct dlist *prev; \};} \ Accessing a field defined in the subclass is done in the identical fashion as in a normal C structure. To access a field defined in the superclass is done by dereferencing through header. So to access the next field given a pointer to a dlist you write: ptr->header.list.next. Note that you should use an access method to reference fields when one is defined, rather than dereferencing, to avoid implementation dependencies. \ \subsection{Packages}\indexi{Packages} Class also provides a mechanism for defining a package of routines. A package is equivalent to a simple class that contains only class procedures and macros. It is defined in a similar fashion except that the word package replaces the word class and only classprocedures and macros categories are allowed. \ Packages are provided by the Class system to allow you to create a set of routines that will be dynamically loaded into a running program. A math library would be one example of a package that could be dynamically loaded but is not associated with a class. \ \formatnote{\section{Compiling and running ATK programs}} \section{Debugging}\indexi{Debugging} This section assumes you are familiar with general debugging techniques \formatnote{< Note: If you are unfamiliar with general software testing and debugging techniques, there a several good books on the subject. For example, Myers, G. J. (1979) \italic{The art of software testing}, John Wiley & Sons, NY, has a good treatment.>} and discusses debugging issues specific to ATK including common problems and their symptoms and debugging tools. \ \subsection{Common problems and symptoms} Experienced ATK programmers can usually find problems relatively quickly because they are aware of the most common problems and their symptoms. The following list is intended to make the accumulation of experience less time-consuming: \ \itemize{ \italic{Null pointers.} One common problem that occurs in ATK programs is calling a method with a \smaller{NULL} first parameter. ATK methods are optimized for speed, so very few check that the parameters passed to them have reasonable values. Depending on the machine, a \smaller{NULL} first parameter will generally dump core. On some machines the program will die immediately upon trying to access the methods associated with the \smaller{NULL} instance. On other machines, the program will try executing code at a random location and die there. \ \formatnote{ \italic{Incompatible versions of a class.} If a module exports one version of a class and another module imports a different version, then it is possible for a method invocation to call the wrong method. This will occur when the second module is not recompiled after the class has been changed and its module recreated. Symptoms can include any of the following: \itemize{ \leftindent{\italic{Incompatible version message.} You get the message \italic{incompatible version of <class> requested}. \italic{Unexpected stack trace.} The stack trace given by the debugger is drastically different from the set of calls you expect to see. \italic{Random core dumps.} randomly. You get core dumps that seem to happen }} If you get any of these symptoms, check the dependencies in the \italic{Makefile} for omissions and fix any, then recompile. If you don't find any omissions but still suspect there are some, your suspicians can be confirmed if a \italic{make clean} followed by a new \italic{make} fixes the symptoms. Note that \italic{make clean} followed by a \italic{make} only fixes the symptoms; you must still locate the missing dependencies in the \italic{Makefile} and fix them. } \italic{Missing method declaration.} If you think an override method should be getting called but it isn't, you may have forgotten to declare it in the \italic{.H} file. Since the object will inherit its parent method, the only symptom is that the method does not get called. \ \italic{Out of memory.} This problem is not confined to ATK applications--any time you allocate memory, the attempt can fail for lack of memory. Nevertheless, because of its architecture, ATK will definitely involve you in memory allocation. If an out of memory failure is a serious error in your application (e.g., your application manages user-entered dynamic data), you may want to test your application for any inadvertant omissions of checks for failure to allocate memory. One time-honored technique, volume testing, involves forcing out of memory conditions by allocating a large block during testing. \ } \subsection{Techniques for tracking down problems} There are two approaches commonly used approaches to tracking down problems in ATK programs: the print statement and a modified \italic{gdb} debugger. \paragraph{The print statement} The \italic{print statement} is a debugging technique in which you place print statements within your code that print out the values of variables or give you information about its flow of control. Print statements have the advantage that you do not need to acquire or learn any additional debugging tools in order to use the technique. Print statements, however, have the following disadvantages: \ \itemize{ \italic{Time consuming.} The program must be recompiled each time you add a print statement. Since ATK programs tend to be small and can be dynamically loaded, this disadvantage is not as great as it is for traditional C programs. In fact, you can sometimes recompile an ATK program more quickly then you can start up a debugger on a large binary. \ \italic{Overwhelming amount of output} If you are not careful, print statements can generate an overwhelming amount of output. If you rely on print statements for debugging, you will probably want to develop a way to turn debugging print statements on and off (through a special commandline invocation, preference menu, or keystroke sequence) and to select "levels" or "areas" of debugging. This is especially important if you are developing an large application that would benefit from permanent debugging code. \ \italic{Interactions with the program.} Sometimes print statements interact with the program and either mask the error or introduce new errors. If the problem is likely to involve some sort of critical time-dependency, then print statements are proably a bad idea. \ } The usual place for print statement output is the \bold{typescript }(or other command window). The default size of the scrollback buffer for \bold{typescript} is 10000 characters. This can be changed by setting \italic{typescript.maxsize} in your \italic{preference} file: \ \example{ typescript.maxsize: <integer> } \paragraph{Debuggers} There are two alternatives for using debuggers when programming ATK: (1) use a debugger that understands dynamically loaded code or (2) compile a version that statically loads your code and then use a standard C debugger. Since the second option results in long delays for compilation/linking, you will probably find the first option preferable. Although it is not distributed with ATK, there is a debugger available that understands dynamic loading, a modified version of the GNU debugger, \italic{gdb}. \ In setting breakpoints, you must remember that a call to a method such as \italic{list_AddElement} will not necessarily call the the method associated with the class name of the method (i.e. \italic{list__AddElement}). The actual method that gets called is the one associated with the first parameter. Thus if \italic{list_AddElement} is called with an instance of \italic{dlist}, then the routine that is called is \italic{dlist__AddElement}. \ Also, in setting breakpoints, you must remember to include two underscores instead of a single underscore. \ Finally, when one module first calls a class procedure from another module, a special piece of code is executed. Some debuggers have problems stepping over this piece of code and stop in an unknown location. Setting a breakpoint either at the start of the class procedure that will eventually get called or at the statement following the class procedure invocation will resynchronize the debugger. \ \formatnote{\formatnote{ 1. Get the ATK debugger. 2. Check with your local site administrator for ATK to see whether you have the ATK debugger. If you need a copy of the ATK debugger: 3. \enumerate{Get a copy of GNU. The GNU debugger is available from anyone who has a copy of it, since the GNU Foundation permits copying, or from the GNU Foundation directly. 1. Get a copy of the ATK modifications to GNU. ATK developers modifed the GNU debugger to deal with dynamically-loaded code. You can obtain the modifications by sending mail to info-andrew@andrew.cmu.edu. } 2}. \formatnote{Get a version of \italic{runapp} with a symbol table. When you are debugging, you need a version of \italic{runapp} that still has its symbol table. To get a version of \italic{runapp}: \enumerate{ Check with your site administrator for ATK to see whether a version is available. If not, recompile \italic{runapp} with the -g option. } 3}. \formatnote{Generate dynamic objects with symbol tables. To use the debugger, your dynamic object files must have symbol tables. To generate dynamic object files with symbol tables from your object files, use the make dynamic object command, \italic{makedo} with a \italic{-g} option: \indent{makedo -g <object files>} The \italic{-g} option produces two dynamic object files for each object file: a \italic{.do} file that does not have symbol table information and a \italic{dog} file that \italic{does} have symbol table information. }\formatnote{ 4. Run the ATK debugger. Assuming the ATK debugger at your site is named \italic{gdb}, to run it, type \programexample{gdb <runapp with symbol table> [optional name of a core file} ] For example, if the version of \italic{runapp} with a symbol table is in /usr/andrew/src, and there is no core file, you type \example{ gdb /usr/andrew/src/runapp } } If you are examining a core file, your code has already been loaded so skip to \italic{Step 7. Load your symbols}; otherwise, you need to do \italic{Step 5. Load your code }and \italic{Step 6. Interrupt runapp.} \formatnote{ 5. Load your code Assuming that you have been using \italic{ezapp} to load your object, you can load your code by typing: \example{ run -d ezapp -d <file that loads your dynamic object> } The \italic{-d} switch to \italic{runapp} causes \italic{runapp} to print out the hexadecimal addresses of all objects it dynamically loads. This is useful for debugging (see below), but not required. On the other hand, the second \italic{-d} switch--to \italic{ezapp}--is required. The \italic{d} switch to \italic{ezapp} specifies that \italic{runapp} should \italic{not} fork the application (see \italic{fork (3)}). Normally, \italic{runapp} does fork, because a fork frees the shell that invoked it for further commands. However, forking interferes with debugging. Thus, when you are debugging, you must prevent forking. If you are using an application other than \italic{ezapp} to load your object, you need to find out the appropriate switch to prevent forking. Most applications use \italic{-d} for this purpose. } \formatnote{ 6. Interrupt runapp. \ }\formatnote{ 7. Load the symbol table for your code. }} \begindata{bp,538559232} \enddata{bp,538559232} \view{bpv,538559232,1,0,0} Copyright 1992 Carnegie Mellon University and IBM. All rights reserved. \smaller{\smaller{$Disclaimer: Permission to use, copy, modify, and distribute this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice, this permission notice, and the following disclaimer appear in supporting documentation, and that the names of IBM, Carnegie Mellon University, and other copyright holders, not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. IBM, CARNEGIE MELLON UNIVERSITY, AND THE OTHER COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL IBM, CARNEGIE MELLON UNIVERSITY, OR ANY OTHER COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. $ }}\enddata{text,538527048}