This file contains the spec for the sample application discussed in the article "Sizing Up Application Frameworks and Class Libraries", by Ray Valdes, DDJ, October 1992. The spec follows the list of files and list of vendor addresses. ================================================================ LIST OF FILES: DDJAFX TXT 37681 09-15-92 10:32p <--The file you are reading now BOROWL ARC 165984 09-15-92 10:15p <--Borland OWL OWLYAO ARC 53664 09-15-92 9:47p <--OWL version by Paul Yao INZAPP ARC 209142 09-15-92 10:21p <--Inmark zApp OBJMNU ARC 496849 09-15-92 9:52p <--Island Systems Object Menu CPPVUS ARC 188334 09-15-92 10:10p <--Liant C++/Views MSMFC ARC 56420 09-15-92 9:34p <--Microsoft MFC ================================================================ VENDOR ADDRESSES: Liant Software Corporation 959 Concord Street Framingham MA 01701-4613 (508) 872-8700 Inmark Development Corporation 2065 Landings Drive Mountain View CA 94043 (415) 691-9000 Island Systems 7 Mountain Rd. Burlington MA 01803 Tel: 617-273-0421 Fax: 617-270-4437 BBS: 617 270 9552 Borland International 1800 Green Hills Road PO Box 6600001 Scotts Valley CA 95067-0001 (408) 438-8400 =================================================================== What follows is the original spec as distributed to vendors by DDJ. =================================================================== CRUISING ABOVE THE API: APPLICATION FRAMEWORKS, CLASS LIBRARIES AND GUI TOOLKITS DDJ AAPI Project Spec V1.0b by Ray Valdes June 25, 1992 Dr Dobb's Journal has begun a project to evaluate application frameworks, class libraries and GUI toolkits. This document presents the project rationale and provides the specs for a sample application. Earlier this month, we circulated a preliminary version of the document and incorporated many constructive comments. Although this document is now at Version 1.0b, there is still some room for judicious revisions. The major change changes since Version 1.0, dated June 23 1992, is the addition Appendices A and B. COMPARING APPLES, ORANGES AND BANANAS Application frameworks are complex and powerful development tools that have generated much excitement among PC programmers. The choice of a framework is as critical to a development project's success as a choice of algorithm or programming language. Frameworks are new enough that no clear standards have emerged, and many plausible contenders exist. Although application frameworks are not a new concept, they have only recently arrived as a mainstream development tool on the PC platform. Many of the key elements in application frameworks were present in Smalltalk-80, now over ten years old. However, it was Apple's MacApp system that rigorously systematized the key ideas of an application framework: a generic app on steroids, providing a large amount of general-purpose functionality within a well-planned, integrated structure. Object-oriented languages are ideal vehicles in which to embody an application framework, and the advent of C++ to the PC platform has allowed mainstream PC programmers to finally enjoy the benefits of frameworks. Class libraries and GUI toolkits are generally smaller and simpler systems than application frameworks. GUI toolkits predate object-oriented languages, and some of the most successful products in this category are written in assembler. Class libraries, on the other hand, incorporate much of the same functions as a GUI toolkit, but do so in an object-oriented fashion and add support for non-GUI functions such as data structure manipulation (queues, lists, arrays, collections). Both class libraries and GUI toolkits often come bundled with design tools that allow you to interactively specify application components, such as dialog boxes and menus. Because application frameworks, class libraries and GUI toolkits represent very different technological approaches, comparing them is like comparing apples, oranges and bananas. Yet the fact remains that many programmers are faced with exactly that decision when starting a development project. As that stage of the project, all these products "look like fruit to me", as the saying goes. All these products address the same basic software development issues: how to reduce coding effort, speed time to market, increase maintainability, add robustness, and leverage off prefab functionality. THE VIEW FROM SQUARE ONE Recently at DDJ, we faced this decision at the outset of the Handwriting Recognition Contest. One of the programs we needed to write was a browser for handwriting data samples. Although this kind of program does not fall within the standard product categories of shrink-wrapped software, many of the functions required by our program are representative of those in modern graphics-oriented applications (see "Program Spec" section later). The view from square one is very simple: do you stay with the raw API (basic DOS or Windows services), or do you choose something at a higher level? If you choose the latter (the "fruit" scenario), then you're at square two: choosing between a framework, a class library or a GUI toolkit. By using an application framework, you can cruise above the API at a high altitude, but have the steepest learning curve. Because the framework constrains your design decisions, you also run the risk of crashing into a brick wall (to mix a metaphor). Using a GUI library, you're much closer to ground level, and enjoy a gentle learning gradient as well as free choice in architectural design approach. The downside is, of course, you must do more of the work yourself. In the case of the DDJ Recognition Contest, our deadline did not permit the uncertainties associated with any kind of learning curve, so we stayed with the ground-level Windows API (we also implemented an even more rudimentary DOS version using our C compiler's graphics library). We wished for the time to properly evaluate development alternatives, and so this project began. DEFINITIONS For our purposes, we define an application framework as an integrated object-oriented software system that offers application-level classes (documents, views, commands) and that is meant to be used in its entirety. An application framework embodies a particular philosophy for structuring an application, and, in return for a large mass of pre-built functionality, the programmer gives up control over many architectural design decisions. The principal design approach used by most application frameworks is an evolutionary descendant of Smalltalk's Model/View/Controller triad. We define a class library as an object-oriented set of workhorse classes that can be incorporated piece-meal into an application, rather than the other way around. By allowing the programmer to pick and choose, a class library does not enforce any particular architectural approach. The services offered by class libraries include both user interface functions (menus, dialog boxes, and graphics primitives) as well as general utility functions (date and time conversion, data structure manipulations, and so on). Finally, a GUI toolkit offers similar services as a class library, but using a procedure-oriented rather than an object-oriented interface. Most DOS-based GUI libraries are at the same basic level of abstraction as Windows (or at least the early versions of Windows). Unlike class libraries, GUI toolkits generally do not offer non-graphical utility routines (queue management routines, for example). In practice, the distinctions between one product and another are not as clear-cut as these definitions. For example, one vendor of a class library directly compares their product against a popular application framework. Another class library vendor avoids comparisons with application frameworks, yet the sample programs bear an uncanny philosophical resemblance to those of the application framework. THE PROBLEM WITH "SATURDAY NIGHT" REVIEWS So how does the programmer make an informed choice? Not by reading most product reviews we've seen. Because much of an application framework's machinery lies under the surface, evaluating application frameworks is a uniquely difficult task. A few hours spent with the manual or looking over example programs is not appropriate for such a complex and subtle tool. As with evaluating a programming language, the best way is to try using it on a development project of nontrivial size. But there are a couple of problems with this approach. The first is the steep learning curve. The second is that many weeks or even months may elapse before running into specific limitations that stymie further development or require contrived workarounds. It may be too late when you discover that your framework is actually a straightjacket. Another problem is that, even if a framework is well-designed and bug-free, it may offer multiple ways of accomplishing a given task, and it is not always clear to the novice which is the best approach. By contrast, the experienced user of a framework knows which methods offer best performance and are easiest to code, but this knowledge is often hard-won. Because the limited hours alloted for a standard product review (typically, an evening with the manual, plus a weekend working with sample code) do not give the reviewer sufficient exposure to real-world use of the framework, we have arrived at an alternative approach. OUR PROPOSAL Our proposal is to evaluate frameworks, class libraries and GUI toolkits by asking vendors (or friendly third parties who are expert with that particular product) to code up a small but non-trivial example program. Briefly, the program we propose is an interactive browser for digitized handwriting samples. Its functional requirements are specified later in this document. Our aim is not a confrontational "shoot-out". Arguing about which is the "best" framework is like arguing about which is the best cuisine, text editor or programming language. Our position is that different products satisfy different needs and tastes. Some products support portability between platforms, others focus on high level of object-orientation, others emphasize closeness to the Windows API, others swear by performance optimization, and so on. Every programmer has a unique set of opinions about which characteristics are important and which can be sacrificed in real-world tradeoffs. Not only that, but strongly-held opinions often change in the course of development, in response to changing market conditions. We think it's possible to arrive at a task that will exercise many of the most interesting features of various frameworks. By implementing the same functional requirements using different frameworks, we provide a concrete example that readers can use in making the decision about whether a framework is appropriate --- each person examining the resulting code in terms of his or her unique tradeoffs. The program as specified is small but nontrivial. Our goal is to arrive at a simple spec that can be implemented by an expert user of a framework in three working days or less. Someone new to the framework, by contrast, may spend a couple of weeks accomplishing the same task. Our specs were not arrived at with any vendor's feature list in mind. Rather, the specs are come from an entirely different project and predate the current effort. The example includes real-world randomness, and as such does not claim to comprehensively exercise every item on a product's feature list. The main value of the example is that its requirements are broadly representative of a large class of graphics-oriented programs. We find it more interesting to get a real-world useful application that works rather than a contrived example that displays twenty different dialog boxes that we'll never use. With this in mind, our specification offers plenty of leeway to the implementor to solve the problem in the way best suited to the application and tool. WHO'LL PASS JUDGEMENT? DDJ will not benchmark the submitted code, nor will we pass categorical judgements on the merits of each submission. Those judgements are left to our readers. We'll publish the code (or as much as will fit into the magazine) and provide an introduction similar to this document. Readers will be able to run the programs, examine the code, check the EXE size, and try to modify it (assuming they possess the necessary tools), and so on. If space permits, we'll point out interesting landmarks and development implications in our tour through the code. We realize that not all systems rely on hand-coded source files. Some vendors offer interactive design tools to accomplish all or part of the task. This is a legitimate approach, as long as it is made clear how much of the process has been automated, what the output looks like (in the case of code generators), and what it would take to modify the results. PROJECT SCHEDULE This project will be done in two parts: for our October and November issues (on Object-Oriented Programming and on User Interfaces, respectively). Although the boundaries are not always clear-cut, the October issue will focus on object- oriented application frameworks and class libraries, and the November issue will cover GUI toolkits and barebones class libraries. The production schedule for our October OOP issue means vendors must submit first drafts to us by July 7th (or thereabouts). Because the time is close at hand, we'd like to hear from participants as soon as possible. For our November issue on user interfaces, the deadline is August 7th. PROJECT PARTICIPANTS We've asked the following vendors to participate (listed in alphabetical order): o Autumn Hill Software (Menuet and Baby Driver II) o Borland (Object Windows Library) o Inmark Development (zApp) o Island Systems (Object Menu for C++) o MetaGraphics (MetaWindow) o Microsoft (MFC) o Liant (C++/Views) o XVT Software (XVT Toolkit) From this list (and other possible additions), it is likely we will have four or five participants in each round of this project. Depending on reader response, we may follow up with a future article featuring additional frameworks, or new versions of existing products. In certain cases where a vendor was unable to participate, we've chosen to rely on a third party developer to implement the example application. We've not tried to be comprehensive in our selection of vendors (there are way too many to cover in one or two issues of the magazine). Rather, our focus remains on technology issues rather than specific products. We've tried to make our list representative rather than comprehensive. Comparing apples, oranges and bananas is a stated goal; comparing all such instances is not. Every participant will receive the same specs and have the same amount of time (from now until July 7th, in the case of the October issue). No participant will see the code of others until it is appears in print. In addition to submitting code, participants can optionally submit a short narrative about their implementation. This would be a maximum of 500 words, and can be used to point out interesting features or clarify one's development approach. Participants are also free to add as many features to the spec as they desire, in order to highlight features of their framework not addressed by the original spec. This is entirely optional. THE PROGRAM SPEC The application is called HWX BROWSER, and allows users to read in different sets of handwriting data from files on disk and display the alphabets in an application window. As stated earlier, the program is not a contrived example meant to exercise items on a vendor's feature list. Rather it is something arising from real-world needs that is in many ways representative of graphics-oriented programs, as well as offering its own unique challenges to the implementor. The browser allows interactive viewing of handwriting samples which have been gathered from a number of people. Each file represents samples collected from a single person, in the form of a series of alphabets. The file has a particular structure, specified in Appendix A. Briefly, it consists of a short header, followed by the data for individual letters of the alphabet, each letter being represented by multiple instances or versions. The data for a single instance is basically a sequence of coordinates as gathered by the digitizer, a set of points for each stroke in a character. Displaying a stroke is easily accomplished by MoveTo() and LineTo() graphics primitives, or by a call to Polyline(). The basic areas of functionality are: file operations, character selection, display of the image in a window, printing, and debugging. Within these basic areas, we've identified some aspects that are optional, given the severe time constraints. FILE OPERATIONS The application must present a list of files to the user, and allow one of these to be made active. Windows 3.1 makes this easy, with the advent of the Common File Dialogs (equivalent to Apple's StdFile dialog). Products that run directly on DOS or on other platforms that don't offer this kind of functionality must implement it themselves. CHARACTER SELECTION The user must be able to identify items of interest in the selected file (after the file has been read into memory by a DDJ-supplied function). The UI mechanism can be a dialog box or a menu or some other technique. Typically, users will want to look at: 1. an individual instance of a particular letter of the alphabet 2. all instances for a given letter of the alphabet 3. an entire alphabet (comprising one sample set) 4. all instances for all alphabets 5. custom sequences of letters, such as a short sentence of phrase displayed in a given alphabet. Of the items on this list, the first two are minimal requirements. The final three items are optional, and probably can't be done within the time constraints. CHARACTER DISPLAY In addition, the user needs to control aspects of the presentation. These aspects include: 1. changing line thickness 2. changing line color 3. changing background color 4. scaling the image 5. scrolling the image The first two items on this list are minimal requirements, the rest optional. Left up to the discretion of the implementor is the choice of UI mechanism: menu bar, dialog box, icon bar, tool palette, pie menu, etc. Scrolling may or may not be necessary, depending on the way letters are presented. A single letter in a single window probably does not need to be scrolled. A fancier alternative would be to display all alphabets in a spreadsheet-like table, with column and row headings (each complete alphabet would be one column). In this case, scrolling around this table is essential, because it won't fit on one screen. Unless the framework offers a built-in tableaux or matrix class, it's likely that most implementors will display a single character per window, given the time constraints. This wide latitude in design choices reflects the leeway we encountered in implementing the program described here: we had no dogmatic preferences about UI widgets, and just cared about getting the job done. Our philosophy is that any program is better than no program, and the world's fanciest empty dialog box is useless compared to a complete rudimentary program that accomplishes the given task. In the case of our DDJ implementations (the raw Windows API and the DOS versions), the results are indeed homely but usable nevertheless. PRINTING The application needs to produce hard copy of letters and/or alphabets. There is much leeway in how printing is implemented: 1. It can be done one letter at a time, similar to a screen dump of a window's contents. 2. It can be done an alphabet at a time, with no pagination or headings. 3. It can be done in a more elaborate report form, with full pagination and headings. Given the time constraints, it is expected most submissions will opt for the rudimentary approach. While we consider printing essential to the program spec, we realize that printing is a problematic issue for some systems, requiring much hand-coding. As with any other part of the spec, if implementing any particular feature will bust the suggested time budget (2-3 days of an expert's programming time) by a large amount, then our advice is to note the fact and pass along to the next feature. A HEX VIEW OF DATA One aspect of a graphics-oriented application that Smalltalk-80 handles particularly well is the ability to provide different kinds of views on a single set of data. In the case of the HWX Browser, this could mean showing both the line drawing of a character as well as its hexadecimal representation in memory (to examine the data compression method, or for debugging purposes). Due to time constraints and our close-to-the-ground approach, we did not implement this feature in our native Windows version of this application. Nor is this a requirement for other implementors. It is a suggested option, for those "real" frameworks whose design allows this feature to be added easily. We'll provide the function that returns a pointer to the binary data, as well as sample code for generating a hex string from binary data (see the section later in this document, "What DDJ Will Provide"). DEBUGGING Debugging support is not an application-level component per se, in that it does not find its way into a shrink-wrapped box or the feature list for the completed application. Nevertheless, it is important issue from the application developer's point of view. Although no mainstream frameworks include debugging support as part of their definition, it is instructive to note that the framework's spiritual ancestor (Smalltalk-80) offered extensive support for debugging in terms of a trace window and object inspector. It is our view that eventually most DOS/Windows frameworks will incorporate some facilities for debugging. Because at present they do not, this feature is entirely optional, for "extra credit". OTHER "EXTRA CREDIT" If any vendors feel that their product has unique features that are not demonstrated by the example application requirements, they are free to add these features to their implementation, and let readers judge for themselves. Examples of additional features may include: 1. special dialogs or controls, such as a color wheel for color selection, or a slider for size specification 2. resizable toolbars or tool palettes 3. animated About box, and so on. 4. spreadsheet-like view of the sampled data (see "Character Display" above). WHAT DDJ WILL PROVIDE We'll supply the data files and the specification for the file format. We'll also supply functions to read the data from the disk into an in-memory data structure, and other functions to display a letter on the screen using MoveTo( ) and LineTo( ) callback functions. In addition to providing the sample data files, we will provide C code that implements the following workhorse functions: * LoadHWXData(fileName) Given a filename, allocates memory and reads data from diskfile into an in-memory data structure, and returns a handle to this data. * RegisterCallback(displayEntity, pMoveTo, pLineTo) This function is passed a handle to a display entity (a window, canvas, grafport or equivalent concept), and pointers to two callback functions. The callback functions are graphics primitives that are equivalent to MoveTo() and LineTo() in the Windows GDI. * GetInstanceCount(charCode) Given the ascii code for a character, return the number of instances loaded into memory for this character. * DisplayInstance(charCode,instanceNumber) Given the ascii code for a character, and a number for a particular instance of this character (enumeration begins at 0), display the character by using previously specified MoveTo and LineTo callback functions. * GetInstanceData(charCode,instanceNumber,pNumberOfBytes) Given the ascii code for a character, a number for a particular instance of this character (enumeration begins at 0), return a pointer to this instance data (for purposes such as a hex dump). Also return the number of bytes in this instance data, by placing a value in a variable whose pointer is passed to this function. * UnloadHWXData() Upon program exit or termination, free up any resources allocated earlier. The specification for the functions listed above is not quite exact. There are minor variations between the DOS and Windows versions. The DOS versions use pointers (large- model far pointers), while the Windows versions use handles from which pointers can be obtained. Consult the actual code for the exact versions. Note that implementors are free to change the code to their liking, as long as functionality is preserved (by basic functionality, we mean accessing the character data so it can be displayed). We won't be offended if our C code is replaced entirely, if this makes things easier for the implementor. Our approach currently suffers from a design flaw in that the entire file of data must be read in before it can be processed; in building an industrial-strength application, such a constraint would be unacceptable. The file structure is described in Appendix A, and the in- memory structure is described in Appendix B. WHAT THE SPEC LEAVES OUT Although we've tried for a representative selection rather than a comprehensive exercise of all features, it's important to note what the spec leaves out. Our focus is on the single-user, graphics-oriented application. Our example does not do anything related to text-editing, multi-form data entry, database access, or multi-user processing. These are important aspects of many real-world applications in the enterprise, and will hopefully be covered in a future round. Even given the limited domain of the application, there are functions used by many single-user interactive graphics applications that our spec does not address. For example: cut, copy, paste, selection by pointing, raster image data, and the all-important undo command. In homage to real-world randomness, we'll have to leave these for another time. THIS IS NOT AN EXAM This is not a programming exam in the sense that there are trick questions or hidden problems to solve. We're only interested in looking at the final result, in understanding what it took to get there, and in discussing the technological issues surrounding frameworks, class libraries and GUI toolkits. Also, we hope this will all provide some good fun. If this spec leaves out any information, it's by accident rather than design. If you have any questions, please do call, rather than attempting to jump through unnecessary hoops. FOR FURTHER INFORMATION If you have any questions, please don't hesitate to contact: Ray Valdes, Senior Technical Editor Dr. Dobb's Journal 411 Borel Avenue, Suite 100 San Mateo, CA 94402 (415) 358-9500 x314 ____________________________________________________________ APPENDIX A: THE FILE STRUCTURE The handwriting data is stored in files that contain multiple sampled alphabets. Within the file, the multiple versions or instances of each letter are organized by ASCII character code rather than by alphabet. Generally, each file contains the handwriting samples belong to one person. For each person, there may be ten or twenty different alphabets in their file. Our DDJ implementation of HWX BROWSER first reads all information from the data file into an in-memory data structure, before allowing access to individual characters. Implementors are free to rewrite the functions we provide, as long as the basic functionality is preserved: accessing the character data from the file and displaying it. The character data file is a binary file, consisting first of a signature word, then a small header, followed by the rest of the data. The digitized stylus points in the handwriting samples consist of two 16-bit signed integers, one for the x-coordinate and the other for the y-coordinate (there is no time stamping, pressure or angle information in the data stream). The data as stored in the file is kept in a very simple compressed form. The simple compression scheme stores the first point in a stroke as a full pair of 16-bit integers. Subsequent points in that stroke are stored as 8- bit deltas off the original point (no provision is made for overflowing the range of 8-bit displacements). The units and coordinate system used in the data is whatever is used by our Wacom 510C digitizing tablet, approximately 200 dots per inch. In the DDJ implementation, we divide the coordinate values by 8 in order to display them on a VGA screen. For future versions, we plan to put appropriate metrics into the file header, to handle a range of devices. The file header consists of a file signature followed by 32 bytes (sixteen words) of mostly unused header (reserved for future use). The file signature value is 0x2121 and is not considered part of the header. The first word following the signature value contains the size in bytes of header. In the current version, it must be 32. This value includes the size of the header-size variable. This is followed by two words, one for the major version number (currently a value of 1), the second contains the minor version number (currently a value of 0). The remaining bytes are unused. The following code excerpt shows how to read the file header. It reads in the values without doing anything with them -- for example, error checking. The actual code does a little bit, but not much more. Here is the excerpt: void ReadHeader(FILE *data_file) // function to read datafile header { extern int ReadWord(FILE *); // calls func to read a 16-bit word int signature,header_size,version_major,version_minor; signature = ReadWord(data_file); // signature value is 0x2121 header_size = ReadWord(data_file);// Number of bytes in header header_size >>= 1; // Bytecount becomes wordcount header_size--; // Subtract the word we just read from hdr size version_major = ReadWord(data_file); //Get version num (major #) header_size--; // Subtract the word we just read from hdr size version_minor = ReadWord(data_file); //Get version num (minor #) header_size--; // Subtract the word we just read from hdr size while(header_size--) // Read the remaining bytes in the header, ReadWord(data_file); // which now consists of unused words } Character data is stored in arbitrary ASCII order, identified by ASCII character codes, which range from 0 to MAX_CHARS (which is #defined to have a value of 127). For each character, there can be a variable number of character prototypes (sample characters or instances). Each character prototype, also known as a gesture, is composed of a variable number of strokes. Each stroke is composed of a variable number of points. The process of reading in the data therefore consists of several nested for-loops -- to read the characters, prototypes, strokes, and points, respectively. The following code excerpt shows how to read the data file. The code in the excerpt calls the previously shown ReadHeader() function. void ReadDataFile(char *inputFileName) // function to read data file { extern void OpenInputFile(char *); // this opens the input file extern void ReadHeader(FILE *); // this reads the file header extern int ReadWord(FILE *); // this reads in a 16-bit word extern int EndOfFile(FILE *); // this checks for end-of-file extern void CloseInputFile(void); // this closes the input file extern void ShowPoint(Point *); // this routine displays a point Point Point; // a struct that holds an (x,y) coord pair FILE *inputFile; // compatible with fopen() int ascii_code; // the ascii code for current character int cur_instance,cur_stroke,cur_pt; // used in nested for loops int num_instances,num_strokes,num_pts;// used in nested for loops // open the data file for input (note that in this example, // routines do not error-check return values; see actual code // for proper error-checking) inputFile = OpenInputFile(inputFileName); ReadHeader(inputFile); // read the header of the data file while (! EndOfFile(inputFile)) // read in variable num of records { ascii_code = ReadWord(inputFile); num_instances = ReadWord(inputFile); if(ascii_code > MAX_CHARS) // if this has a value of > 127 break; // then stop reading the file /*--------------Read in the instances-------------------*/ for (cur_instance= 0; cur_instance < num_instances; cur_instance++) { num_strokes = ReadWord(inputFile); /*--------Read in the strokes for this instance-------*/ for (cur_stroke = 0; cur_stroke < num_strokes; cur_stroke++) { num_pts = ReadWord(inputFile); // read in the first point Point.x = ReadWord(inputFile); Point.y = ReadWord(inputFile); ShowPoint(&Point); /*--------Read in subsequent points-------*/ for (cur_pt = 1; cur_pt < num_pts; cur_pt++) { int word; char *ptr; // read in a word and unpack it word = ReadWord(inputFile); ptr = (char *)&word; // the delta-values apply to previous point Point.x += ptr[1]; Point.y += ptr[0]; // display the point ShowPoint(&Point); }/*points for-loop*/ }/*strokes for-loop*/ } /*instances for-loop*/ }/*while-loop*/ CloseInputFile(); // all done } ____________________________________________________________ APPENDIX B: THE IN-MEMORY STRUCTURE Variable length information read from the file is stored in an in-memory data structure. In DOS, this is an array of pointers to variable-length List structures. In Windows, this is an array of handles to variable-length List structures. The List structure is declared as follows: typedef void *GenericType; // arbitrary type typedef struct { int num_items; // number of items in list GenericType items[1]; // variable length array } List; typedef List far * lpList; // far pointer to a list structure typedef lpList far * lpLpList; // pointer to pointer to list Note that although this is called a List structure, it is more properly known as a variable length array. There are no link pointers. Nevertheless, the usual list operations (such as car, cdr, cadr and so on) can be accomplished on this variable-length array. In DOS, the List structures are constructed using malloc(), calloc() and realloc(). They are freed using free(). In Windows, the API memory management routines are called. To illustrate how the character instances are accessed, here is a code excerpt that frees up the list of lists: void FreeMemStructures(void) { int i,j,k; for (i = 0; i < MAX_CHARS; // MAX_CHARS is #defined to be 127 i++) { if (CharData[i]) // if the pointer is not null { // then get each member of the list for (j = 0; j < CharData[i]->num_items; j++) { // access the sublist that hangs off current element lpList L = CharData[i]->items[j]; for (k = 0; k < L->num_items; k++) { mem_Free(L->items[k]); } mem_Free(L); } mem_Free(CharData[i]); CharData[i] = NULL; } } } In Windows, the code is more complicated, because it uses Windows3-compatible handles rather than pointers, and does not use realloc(). The results are, however, effectively the same. END OF APPENDIX B ================================================================