C++ Database Development 1558283579

418
C++ Database Development 9 Featuring PARODY-the Persistent Almost-Relational Object Database Manager * Persistent objects * Offers full C++ source code AL STEVENS

Transcript of C++ Database Development 1558283579

Page 1: C++ Database Development 1558283579

C++Database

Development

9 Featuring PARODY-the Persistent Almost-Relational

Object Database Manager

* Persistent objects

* Offers full C++ source code

AL STEVENS

Page 2: C++ Database Development 1558283579

C++ DatabaseDevelopment

2nd edition

Al Stevens

A Subsidiary ofHenry Holt and Co., Inc.

Page 3: C++ Database Development 1558283579

Copyright 1994 by MIS:Pressa subsidiary of Henry Holt and Company, Inc.115 West 18th StreetNew York, New York, 10011

All rights reserved. Reproduction or use of editorial or pictorial content in any manner isprohibited without express permission. No patent liability is assumed with respect to theuse of the information contained herein. While every precaution has been taken in thepreparation of this book, the publisher assumes no responsibility for errors or omissions.Neither is any liability assumed for damages resulting from the use of the informationcontained herein.

Second Edition-1994

Stevens, Al, 1940-C++ database development / Al Stevens. -- 2nd ed.

p. cm.Includes index.

ISBN 1-55828-357-91. C++ (Computer program language) 2. Database design.

3. Object-oriented databases. 4. PARADY. I. Title.QA76.73.C153S72 1995005.75--dc20 9436834

CIP

Printed in the United States of America.

1098765432 1

Trademarks

Throughout this book, trademarked names are used. Rather than put a trademark symbolafter every occurrence of a trademarked name, we used the names in an editorial fashion

only, and to the benefit of the trademark owner, with no intention of infringement of thetrademark. Where such designations appear in this book, they have been printed withinitial caps.

Publisher: Brenda McLaughlinManaging Editor: Cary SullivanDevelopment Editor: Debra Williams CauleyCopy/Tech Editor: Betsy HardingerProduction Editor: Stephanie DoyleAssoc. Production Editor: Joseph McPartland

Page 4: C++ Database Development 1558283579

To Katie Margaret Stevens and Jesse Collin Stevens

Page 5: C++ Database Development 1558283579

Contents

Chapter 1: Introduction ..................... 1C++ and D atabases ................................................................... 2The Organization of C++ Database Development .................... 4What You Should Already Know and Have .............................. 5PARODY and W indows? ......................................................... 6Record and File Locking? ........................................................ 7H ow to G et H elp ..................................................................... 7The Latest Version of PARODY .............................................. 8Your Rights to the Software ..................................................... 8

Chapter 2: An Introduction toObject-Oriented Programming........... 9

T he Basics ............................................................................... 10Procedural Programming ....................................................... 11Object-Oriented Programming ............................................... 11The Object-Oriented Program ................................................ 12T he O bject .............................................................................. 13A bstraction ............................................................................. 14

V

Page 6: C++ Database Development 1558283579

vi C++ Database Development

Encapsulation .......................................................................... 15M ethods and M essages ............................................................ 17

Functional M ethods ........................................................ 17Data Type M ethods .......................................................... 18Implicit Conversion M ethods .......................................... 18M em ber Functions .......................................................... 20

Inheritance ............................................................................... 20Single Inheritance ............................................................ 21M ultiple Inheritance ........................................................ 22

Class Relationships .................................................................. 23Specialization .................................................................. 23Com position .................................................................... 24Collaboration ................................................................... 24M anagem ent .................................................................... 24

Polym orphism ......................................................................... 24Sum m ary ................................................................................ 26References ............................................................................... 26

Chapter 3: A Brief History ofDatabase Management ................... 27

The Database M anagem ent System .......................................... 28The Data Definition Language ........................................ 29The Data M anipulation Language ................................... 29The Query Language ........................................................ 30The Report W riter Language ............................................ 30

Database M odels .................................................................... 30Proprietary Data M odels .................................................. 30The H ierarchical Data M odel ......................................... 31The N etwork Data M odel ................................................ 32The Relational Data M odel .............................................. 33The O bject Data M odel .................................................. 36

Sum m ary ................................................................................ 39References ............................................................................... 39

Page 7: C++ Database Development 1558283579

Contents vii

Chapter 4: C++ and Persistence. .. 41

File Input/O utput in C ............................................................ 41File Input/O utput in C++ ........................................................ 42The Problem with C++ Persistent Objects ............................... 43

The Size of the O bject ...................................................... 44N on-Persistent M embers .................................................. 47Pointers in the O bject ...................................................... 47References in the O bject .................................................. 48Format-Defining Related Objects .................................... 48Em bedded O bjects .......................................................... 48Objects from Class Libraries ............................................ 49Virtual Functions ............................................................ 49

C opies of O bjects ..................................................................... 50Sum m ary ................................................................................ 50R eferences ............................................................................... 51

Chapter 5: Solving the PersistentObject e, ............................................ 53

A lternative Solutions ................................................................ 54Custom File Input/Output Methods ................................. 54Extend the C++ Language ............................................... 55W rite a Preprocessor ........................................................ 56Limit the Scope of a Persistent Class ............................... 56Use a Relational DBM S .................................................... 56The Persistent Class Participates in Its Own Persistence ....... 57

Objectives of a Persistent Object Database .............................. 57O bject Identity ................................................................ 57M aintenance of O bjects .................................................. 58Copies of O bjects ............................................................ 60

Sum m ary ................................................................................ 61R eferences ............................................................................... 61

Page 8: C++ Database Development 1558283579

viii C++ Database Development

Chapter 6: The Object Databaseand the Relational Database ........... 63

The Object Database Model ................................................... 64The Relational Database Model .............................................. 65Choosing a Database Model ................................................... 67Adapting the Relational Model to Objects ............................... 69

The Database Schema ..................................................... 69Retrieving O bjects ............................................................ 70A dding O bjects ................................................................ 71Changing an O bject ........................................................ 71D eleting O bjects .............................................................. 72Maintaining Object Integrity ............................................ 72N avigating a Class .......................................................... 73N avigating Am ong Classes ................................................... 74

Sum m ary ................................................................................ 75R eferences .............................................................................. 75

Chapter 7: Designing an ObjectDatabase ....................................... 77General Design Guidelines ...................................................... 78The 10 Steps of Object Database Design .................................. 79Identify the Basis ..................................................................... 79

Starting from an Existing Solution ................................... 80Starting from Scratch ..................................................... 81W rite Down the Basis ..................................................... 81

Define the Requirem ents ......................................................... 81Functional Requirements ................................................. 82Performance Requirements ............................................. 83

Identify the Data Item s .......................................................... 83Separate the Data Members from the Classes .......................... 85

Page 9: C++ Database Development 1558283579

Contents ix

Build the Data M em ber Dictionary .......................................... 86Gather the Data M embers into Classes ................................... 87Identify the Key M embers ........................................................ 93

Primary Keys ..................................................................... 94Secondary Keys ................................................................ 96

Identify the Class Relationships ............................................... 97Identify the M ethods ................................................................. 98M ake the Class Persistent ........................................................ 99Summary ................................................................................... 101References .................................................................................. 101

Chapter 8: PARODY ...................... 103PARODY Class Hierarchy ......................................................... 103Defining a PARODY Database .................................................. 105

Declaring the PARODY Database ...................................... 105Defining the Persistent Object Class ................................... 106Defining Keys ................................................................. IllSpecialized Keys ................................................................ 112String Keys ......................................................................... 115Secondary Keys .................................................................. 116Concatenated Keys ............................................................. 117Relating Classes ................................................................. 117M ultiple-Copy Objects ....................................................... 119Persistent References to Persistent Objects ......................... 122Using the PersistentObject<T> Class .................................. 125

How PARODY Brings It Together ............................................. 126Opening the PARODY Database ....................................... 127Constructing a Persistent Object ........................................ 127Destroying a Persistent Object ........................................... 130Object Input/Output: An Overview .................................... 132Integrity Checks ................................................................. 133

Summary ................................................................................... 133References .................................................................................. 133

Page 10: C++ Database Development 1558283579

x C++ Database Development

Chapter 9: Using a PARODYDatabase ..................................... 135

Building a D atabase ................................................................... 135

M anaging Persistent O bjects ...................................................... 136W orking w ith K eys ............................................................ 137Retrieving Persistent O bjects .............................................. 139Creating Persistent O bjects ................................................ 139Changing Persistent O bjects ............................................... 140D eleting Persistent O bjects ................................................. 141Integrity ............................................................................. 14 1

N avigating the D atabase ............................................................ 142Navigating by Key: Content-Addressable Objects .............. 142Navigating by Address: Position-Addressable Objects ........ 146N avigating Shorthand ........................................................ 149

Converting PARODY Databases ................................................ 150A dding C lasses ................................................................... 150R em oving C lasses .............................................................. 150Adding Data Items to an Existing Class ............................. 150Removing Data Items from an Existing Class .................... 151Adding Keys to an Existing Class ....................................... 151Converting the D atabase .................................................... 151

Rebuilding the Index File ........................................................... 153D atabase Integrity Errors ................................................... 153The Index Rebuilding Program .......................................... 153

Sum m ary ................................................................................... 155

(ha pter 10: PARODY inAppfication s .................................. 157U tility C lasses ............................................................................ 158

T he string C lass ................................................................. 159T he bool C lass ................................................................... 159

Page 11: C++ Database Development 1558283579

Contents xi

LinkedList<T> ................................................................... 160The Date Class ................................................................... 161The M oney Class ............................................................... 161

The G UI Class, a Generic User Interface ................................... 161The Display ........................................................................ 162The Keyboard .................................................................... 163The gui Object ................................................................... 163M enus ................................................................................ 166

N avigational Access: The Payroll Application ............................ 169Associative Access: The Game of Life Application ..................... 171

The Concept of Life ........................................................... 171An Evolution of Generations .............................................. 172The Life Program ............................................................... 172Rules for Survival ............................................................... 173Running the Program ......................................................... 173

Almost Relational: The Personnel Application ........................... 175The Department Class ....................................................... 177The Employee Class ........................................................... 177The Project Class ............................................................... 177The Assignment Class ........................................................ 178The M anager Class ............................................................ 178The Personnel Program ...................................................... 179The Project View Program ................................................. 181Rebuilding the Index .......................................................... 183

Persistent References: The Family Tree Application ................... 183The FamilyM ember Class ................................................... 183M aintaining a Family Tree ................................................. 184

Summary ................................................................................... 185

Appendix A: Building the Software ..... 187General Instructions ................................................................... 188

Install Your Compiler ........................................................ 188Install the PARODY Source Code ...................................... 188M odifying M akefile ........................................................... 189

Page 12: C++ Database Development 1558283579

xJJ C++ Database Development

Building the Executable Program s ...................................... 189Testing the Software ................................................................... 189

AN SI.SYS ........................................................................... 190Run the Exam ple Application Program s ............................ 190Rebuild the Indexes ............................................................ 191

Listings ...................................................................................... 191M akefile ............................................................................. 191Parody.bld .......................................................................... 193

Appendix B: PARODY ReferenceGuide ........................................... 195

M odifying PARODY ................................................................. 195Constants ........................................................................... 195typedefs .............................................................................. 196

Class Library Reference ............................................................. 197Exceptions Thrown .................................................................... 212

Program Error Exceptions .................................................. 212Persistent* Exception ......................................................... 213G UI Exceptions ................................................................. 213

Disk Data Structures .................................................................. 213Disk Files ........................................................................... 213N odes ................................................................................ 214Persistent O bjects ............................................................... 216Indexes ............................................................................... 217

References .................................................................................. 221

Appendix C: Software Listings ....... 223Glossary ....................................... 389Index ........................................... 399

Page 13: C++ Database Development 1558283579

Preface

In 1987, I wrote the first edition of C Database Development, a bookthat describes how to build database software applications with a subsetof the relational data model implemented in the C language. That bookincluded the C source code for a database management system andimplemented a design approach in which the programmer uses the fea-tures of C to define an application's database. In the approach taken bythat book, C serves the traditional functions of the data definition anddata manipulation languages, which are usually implemented as separatelanguages in formal DBMSs.

In 1991, after the ANSI X3J11 committee completed the standarddefinition of C, I wrote a second edition of C Database Development,which used the same methods but with Standard C. Both editions of thatbook continue to be widely read, and programmers throughout theworld have used the software in these books to develop many diverseapplications. The principle is tried and proven.

The first edition of C++ Database Development in 1992 continuedthat tradition with the C++ programming language and the object-orient-ed data model-not with a wrapper around the old C function library,but instead with a completely new C++ class library called PARODYthat uses the object-oriented features of C++ to implement what hascome to be known as persistent objects.

This book is the second edition of C++ Database Development. Itupdates PARODY's source code to incorporate into PARODY II the lat-est features of C++ as they are being defined by the ANSI X3J16 com-mittee. Those features include templates, exception handling, an

XIIi

Page 14: C++ Database Development 1558283579

xiv C++ Database Development

improved casting mechanism, and run-time type identification. Althoughthe first two features were defined in the C++ Annotated ReferenceManual when I wrote the first edition, they were not implemented in themore popular compilers for the PC. The definition of templates haschanged significantly since then, and several compilers now implementtemplates and exception handling. The Borland compilers for MS-DOSand OS/2 implement most of the new features, and, by the time this bookreaches publication, so will others.

C++ is still evolving. I am writing this edition in the summer of 1994,and the X3J16 committee is still considering major additions to the lan-guage. Its procedures involve publication of a draft standard document towhich the programming community at large reacts. The draft is due to bepublished late this year. The process of public reaction and committeeresponse takes a long time, more so in this case because many C++ lan-guage changes have been proposed. Compiler vendors try to keep pacewith the evolving draft standard, but this one is a constantly shifting tar-get; the committee is innovating a lot of language changes. The languagefeatures that I used for PARODY II reflect the ones that were accepted bythe committee in March of this year and that have been implemented byBorland in its MS-DOS and OS/2 compilers. Some of these features canand most likely will change. For example, the first edition of this bookused a String class that I designed. Since then, the committee has accepteda string class that is substantially the same as mine but with some minordifferences. Borland implemented the ANSI string class in its compilers,and I substituted its class for my own in PARODY II. Now X3J16 is con-sidering a different string class that uses new template features andaccommodates strings of various character widths. It is not clear how thewhole issue will shake out. Eventually, when the newer features are avail-able in compilers, there may be a third edition of C++ DatabaseDevelopment.

In addition to upgrading PARODY to use new C++ language fea-tures, I have made changes to how PARODY itself works and how youuse it, reflecting reports and requests from readers. I have also added tothe example applications to fill in some gaps that I perceived based onquestions you asked.

By using the techniques presented in this book, you can define andbuild an object database in C++. The programs that you write use abstrac-tion to define the database, inheritance to create persistent objects, andpolymorphism to assist in their own persistence. In short, they are object-oriented database programs. This book contains the necessary C++ sourcecode, a valuable tool for the development of applications. As you read the

Page 15: C++ Database Development 1558283579

Preface xv

book and use the software, you will learn about persistent objects, object-oriented databases, and object-oriented programming.

Object-oriented programming languages have been in use since the1970s. C++ saw its first light as the object-oriented extension to C about15 years ago within AT&T. In recent years, C++ compilers for personalcomputers have proliferated, and C++ is rapidly replacing C as the pro-gramming language of choice for new development.

Database management technology dates back to the 1960s, when thefirst general-purpose file management utility packages were developed.Throughout its history the data processing industry has refined andimproved the formal database models and the techniques and proceduresthat implement them, culminating in the object-oriented data model.

This book brings together C++ and the persistent object data modelto provide all the tools-with source code-that you need to build persis-tent object databases.

Al Stevens1994

Page 16: C++ Database Development 1558283579

Chapter

IntroductionShe did not cease to wonder at the persistence of theunforeseen...

-Thomas Hardy

This book and its software deal with C++ persistent objects. The perva-sive growth of object-oriented programming technology has made it thechosen technique for software development in the 1990s, and C++ hasexperienced similar growth, becoming the language of choice for writingobject-oriented programs. Persistent objects are one facet of the many-faceted object-oriented programming paradigm, but one that is not andhas never been supported by the C++ programming language. To havepersistent objects in a C++ program, programmers must implementthem, either by using a database management system or by writing thepersistence code.

This book adds persistence to C++ programs by providing supportfor persistent object database management. This is not to imply thatobject-oriented programs have had to do without database managementfor all this time. To the contrary, traditional database management tech-niques still work as well as they always have, and you can integrate

I

Page 17: C++ Database Development 1558283579

2 C++ Database Development

them effectively into object-oriented programs. However, the traditionalmethods do not fully exploit the wealth of expression that object-orient-ed programming brings to a software development project. There areseveral available commercial object-oriented database management sys-tems. You have more choices now than before. One of them-thisbook-brings the technology directly to the programmer in a well-docu-mented class library with source code and discussions on the implemen-tation of the persistent object database management system and itsunderlying principles.

C++ Database Development is, therefore, about building databasesoftware applications by using the features of the C++ programming lan-guage. By carefully reading the text, studying the source code, and run-ning the software, you will learn how to design and implement a persis-tent object database application. You will use a persistent object systemcalled PARODY II (Persistent, Almost Relational Object Database),which later chapters explain fully. The complete PARODY II source codeis on the companion diskette included with this book.

Before proceeding into the depths of object-oriented design anddatabase design, we should dispense with one anomaly in our industry'suse of the English language. The word hierarchy refers to an organiza-tion of ranking in which each level is subordinate to the one above. Thetraditional language of database management defines a hierarchy as anorganization in which a member has only one superior-a parentrecord-but may have multiple subordinates-child records-reflectingthe ancient clerical use of the word. An ordering of rank in which achild has multiple parents is a network. That distinction defines bound-aries between two different database models. Object-oriented designmakes no such distinction, however. Class systems with single inheri-tance and those with multiple inheritance are called hierarchies. Whenwe mix the technologies, though, the varying usages can confuse us.This book tries to keep the differences clear.

C++ and DatabasesC++ is an object-oriented superset of the C language. By using C++'sobject-oriented extensions, a programmer can define new data typesand build a class architecture that orders data types into a well-formedhierarchy.

Page 18: C++ Database Development 1558283579

Chapter 1: Introduction 3

The concept of the database record format, which dates to the earliestdays of data processing, is enhanced by the C++ facility that allows a pro-grammer to build a new data type by defining a class. The class describesthe database record. If your database records essential data about a com-pany's departments, for example, then there is probably a Departmentclass that describes the data that the application needs to support depart-ment records.

That approach is similar to traditional database definition but withthis significant exception. Design approaches prior to object-orienteddesign defined only the data representation of a database record andnone of its behavior. All processing of the data-which, in the object-ori-ented view, determines a data record's behavior-was defined in theapplication programs themselves. Different application programs werefree to define different and contradictory behavior for the same data,which is a weakness of that classic approach. Separation of data repre-sentation and behavior produces a potential for data disintegrity and,what is more, does nothing to prevent it.

Object-oriented design defines data's behavior and representation inone encapsulated unit called a class. Application programs that are them-selves outside the class's definition use, but do not specify or otherwisechange, the class's behavior. You will learn what that concept means, howit works, and its consequences beginning in Chapter 2.

What C++ does not include as an intrinsic part of the language is theability to automatically store and retrieve instances of classes-objects-onto and from persistent storage media. In other words, C++ does nothave a built-in database management system for storing and retrievingrecords in files. That's not surprising. Neither does C. Applications writ-ten in C that require record storage and retrieval use either a general-purpose database management function library or custom file input/out-put functions. C++ applications could use similar techniques, and manyof them do. They read and write the data values defined in the class defi-nitions of the objects. There are, however, extensions to C++ classes thatgo beyond traditional C structure declaration, and these extensions cre-ate their own unique problems with respect to data storage and retrieval.Furthermore, the concepts of an object-oriented data store offer thepotential to support requirements that are not particularly well support-ed by traditional database management techniques. So the evolution ofobject-oriented technology has spawned a new data model-the objectdatabase-with the C++ language as a sound vehicle for building andusing the model.

Page 19: C++ Database Development 1558283579

4 c++ Database Development

The Organization of C++ DatabaseDevelopmentThe chapters that follow teach you the concepts of persistent object data-base design and how to build your own object database by using thePARODY software.

Chapter 2 provides a brief introduction to object-oriented program-ming. This is not a comprehensive treatment of the subject, which wouldtake a volume of its own. Instead, the chapter defines the terms commonto object-oriented programming and relates them to the architecture ofobject-oriented computer programs. The discussion addresses object-ori-ented programming as it applies to C++. The C++ object-oriented exten-sions to C do not, strictly speaking, support so-called pure object-orient-ed programming as defined in other programming languages. Yet C++ isthe leader among object-oriented programming languages, and itsapproach to the technology is the one most programmers are using andthe one you will use with the concepts taught by this book.

Chapter 3 is a brief history of database technology. It discusses the tra-ditional hierarchical, network, and relational data models and introducesthe object database model. As you will learn, students of object-orientedtechnology often confuse the hierarchical parent-child relationships ofdata files and those of base and derived C++ classes, perhaps because thetwo kinds of relationships can look alike in design diagrams. You mustkeep these distinctions clear, however, and Chapter 3 helps you do that.

Chapter 4 is about C++ and persistence. The chapter discusses howC++ programs store data structures on disk and how those traditionaltechniques do not work with persistent objects in a class inheritance,object-oriented design.

Chapter 5 defines the objectives for persistent objects. The chapteroffers alternative solutions to the problems posed by Chapter 4, and itidentifies objectives for persistent object database management.

Chapter 6 discusses the object-oriented and relational database mod-els, what they have in common, how they differ, and how traditionalrelational design methods can be valid for object-oriented design. Thechapter also discusses when to use a relational database management sys-tem and when to use the object database model.

Chapter 7 describes a step-by-step approach for designing an objectdatabase to support the functions of an application. These steps involvemore than designing and writing code. You must learn the functional

Page 20: C++ Database Development 1558283579

Chapter 1: Introduction 5

application, derive the requirements for automating parts of it, and buildeach class that goes into the design.

Chapter 8 introduces PARODY II-the Persistent, Amost RelationalObject Database management system-hereafter called simply PARODY.You will learn how PARODY works and how to use the features of C++class design to define the schema for an application's database-the datadescription language (DDL) of traditional database management. Youwill learn to integrate the classes that describe your database with thePARODY base classes to add persistent behavior to your objects.

Chapter 9 describes how a C++ application interacts with the objects in aPARODY database-how to create the database, add new objects, retrieveexisting objects, modify them, and delete them from the database. This chap-ter describes the application program interface (API), also called the datamanagement language (DML) in the parlance of database management.

Chapter 10 presents examples of programs that use PARODY. Thechapter includes some utility classes and several example applicationsthat use the PARODY database management system. The examplesinclude source code to define the respective databases and implement theexample applications. You will use these applications as examples in thedesign and implementation of your own applications.

Appendix A contains the instructions for building the PARODY classlibrary and the case study applications. The appendix includes makefilesand instructions for using contemporary C++ compiler products that rununder MS-DOS and OS/2.

Appendix B is the PARODY reference, including with descriptions ofeach of the PARODY classes and their application program interfaces.The reference guide includes examples of each of the class methods.

Appendix C has the source listings for the PARODY database man-agement system. These are provided for reference during the technicaldiscussions that appear elsewhere in the book. You should not buildPARODY by typing in the code; the book includes a diskette. You candownload newer versions from CompuServe, as described below.

What You Should AlreadyKnow and HaveThis book assumes that you have a rudimentary understanding of theC++ programming language. I recommend my book, Teach Yourself C++,

Page 21: C++ Database Development 1558283579

6 c++ Database Development

4th Edition, MIS Press, for C programmers who want to learn C++. Youshould have a good handle on C++ before you attempt an understandingof the techniques presented here. A reference list at the end of each chap-ter lists programming texts that apply to the subjects at hand.

To use the software, you need one of the compiler products discussedin Appendix A. These compilers build the software with the MS-DOSand OS/2 operating systems. You might use a different compiler and adifferent operating system. The software is independent of any particularplatform, but your compiler must support templates, exception handling,run-time type information, and the new typecast mechanisms. Your bestbet is to use a Borland C++ compiler. I developed this code with BorlandC++ for OS/2, version 1.5. I tested it with Borland C++ 4.0 for MS-DOS.Until the specifications for new language features are published,reviewed, and approved, the likelihood is slim that any two compilersfrom different vendors have identical implementations.

As language features change and more compilers support them, I willupgrade PARODY II. See the discussion titled "The Latest Version ofPARODY" later in this chapter to see how to get upgrades.

The example applications employ a generic user interface that usesthe standard C++ iostream class. Chapter 10 describes the generic userinterface. This interface is independent of any computer or operating sys-tem. Most C++ implementations support it.

PARODY and Windows?Readers of C Database Development and the first edition of this bookoften ask when I plan to integrate the two database management systemsinto Windows or with the screen management software I published inother books. I have no such intentions. PARODY is independent of othersystems software beyond the requirement for a standard C++ compiler.You can integrate it with the user interface of your choice. It is bound tono particular architecture, and there are no portability layers for you torewrite. It manages the database and leaves the user interface to whateverapplications framework you use.

I am often asked about implementing PARODY as a Windows orOS/2 dynamic link library (DLL). It is possible but would be cumbersometo implement. The database management software is implemented as aclass library with interface member functions. Installing those functions

Page 22: C++ Database Development 1558283579

Chapter 1: Introduction 7

into a DLL from a Windows application would require two class sys-tems-one to provide the DLL calling convention layer and one to imple-ment the database manager. Keeping those two class systems synchro-nized could prove to be difficult.

Record and File Locking?Many readers have asked about record and file locking to support multi-user environments, multitasking, and concurrency. Although those arenoble and worthy objectives and certainly achievable, I have never con-sidered pursuing them. The problems that I solve with these tools tend tobe concentrated in single-user environments. Adding concurrency to adatabase manager adds a level of complexity far greater than that whichyou can readily publish in a book.

How to Get HelpThe best-and often the only-way to get in touch with me is throughthe CompuServe Information Service, a national on-line system with elec-tronic mail and many topical forums where members share their knowl-edge and experience. I log onto CompuServe every day and will answeryour questions about this book and its software. I am sorry, but I cannotpromise to answer letters or return phone calls. Please do not send mesamples of your source code or the output from compilers and makefilesunless I ask for them after we've discussed your problem.

In addition to giving you access to me, CompuServe is the official on-line support facility used by most software vendors, including the vendorsof the compiler products discussed later in this book. All you need is apersonal computer with a modem and communications program and aCompuServe account. Most bookstores can sell you a kit that has whatyou need to open a CompuServe account. Once you have an account, youcan log on, go into the MAIL section, and post messages directly. Myaccount number is 71101,1262.

You can find help on CompuServe from the experts on Borland C++,Microsoft C++, Windows, DOS, UNIX, GNU, OS/2, and almost any otherdevelopment or operating environment that you might use. The technicalsupport available on CompuServe far outstrips any such support you get

Page 23: C++ Database Development 1558283579

8 C++ Database Development

from a vendor's toll-free support number. CompuServe is as important atool to a programmer today as are a good editor and debugger.

Other electronic mail delivery systems such as INTERNET have gate-ways to CompuServe, and you can write to me through those gateways ifyou initiate the dialog and send me your E-mail address.

The Latest Version of PARODYI maintain PARODY in a file you can download from CompuServe's DDJForum, which supports Dr. Dobb's Journal readers. Log onto that forumand search for files with the keyword PARODY.

Your Rights to the SoftwareThe source code in this book is for use by anyone for the development ofcomputer software applications. You can make as many copies of thesource code as you need for yourself and the other programmers on theteam. Those who use the code will, I hope, have their own copy of thebook to help them along.

You may build and distribute applications by compiling and linkingwith the code that you get from this book. There are no royalties for itsuse, and I do not require you to acknowledge my contribution to yoursystem. If the conditions under which you distribute your programsrequire you to include source code, buy a copy of this book for each ofyour customers.

You may not publish the source code in a book, article or other tech-nical journal. If you are publishing an application that uses PARODY,please tell your readers where they can get the book and the code.

Page 24: C++ Database Development 1558283579

Chapter 2

An Introduction to Object-Oriented Programming

Knowledge is the conformity of the object and the intellect.

-Averroes

In the 1990s, object-oriented programming and C++ in particular havebegun to dominate the software development industry. C++ has beenavailable in UNIX installations for years, and now most of the C com-pilers sold for MS-DOS systems include a C++ compiler. Many othercontemporary software development systems use the object-orientedparadigm as their basis. As pervasive as object-oriented programminghas become, many programmers still do not understand it. This chapterpresents to those programmers a brief introduction to object-orientedprogramming.

This book assumes that you have a working knowledge of C++. Thatexperience alone has exposed you to the object-oriented paradigm. If youhave built and used C++ classes, even in a tutorial or classroom exercise,then you have already written some object-oriented programs. There are,however, many terms that you should understand so that you can relate

0

Page 25: C++ Database Development 1558283579

1 0 C++ Database Development

your code to the discussions around you. Simply writing a C++ programdoes not qualify a programmer as an expert on object-oriented technolo-gy. There are certain rules and procedures to follow, and there are certainbenefits to be gained by understanding and following the discipline ofobject-oriented programming.

An author of traditional procedure-oriented programs does not intu-itively understand object-oriented programming. The notations andapproaches to design are different from what you have learned and usedin the past. Furthermore, explanations such as the one in this chapter donot usually complete the understanding of object-oriented programming.You need reinforcement-both from experience and from feeling thatthere is something to be gained from a new and different approach.Programmers might understand the concept at the intellectual level andyet not accept it as a pragmatic approach to programming simplybecause they have been writing good programs for a while and see nocompelling reason to tamper with that success. For this reason, it is diffi-cult to teach object-oriented programming, although it is not difficult tolearn. Learning object-oriented programming is a process of discovery.Teaching it, therefore, becomes the management of that process.

Many of the current generation of programmers have learned andaccepted object-oriented programming as a better way to express soft-ware algorithms. Most of them started from the procedural paradigm,having programmed in traditional languages such as C. Those who makethe switch become advocates of the object-oriented approach. Enough ofthem have done so that the rest of us cannot deny that there is somethingto it. You can take a lesson from the observation that once a programmertakes the plunge, he or she almost always becomes a convert. Therefore,the best way to learn object-oriented programming is to try it. Untilrecently, however, the tools were not widely available. Now, virtuallyevery PC can have a C++ compiler, and virtually every programmer hasaccess to the tools.

The BasicsThe first understanding of object-oriented programming is to be found inthis design guideline:

The expression of an algorithm should model the applica-tion domain that it supports.

Page 26: C++ Database Development 1558283579

Chapter 2: An Introduction to Object-Oriented Programming 11

This concept reflects an older one that states that the solution to a prob-lem should resemble the problem that it solves-a guideline that allowsobservers of a solution to recognize its purpose without necessarilyknowing in advance about the problem. When you see a properlydesigned word processor, you intuitively understand that the problembeing solved is one of capturing and manipulating text. When you see aproperly designed inventory management system, you recognize that itspurpose is to maintain a record of stock quantities and locations. Yourecognize those things because the designs resemble and therefore remindyou of the problems that they solve.

Carrying this concept to a higher level of abstraction, you recognizethat the purpose of a programming language is to express with algo-rithms the solution to a data processing problem. The techniques used inthat expression determine how successfully the solution models its prob-lem domain. Object-oriented programming is one of several differentapproaches to the expression of algorithms, and it is often misunder-stood-primarily by those who do not use it.

Procedural ProgrammingIn the classic approach to programming, a programmer designs a set ofdata structures followed by functions and procedures to process the data.This approach is called procedural programming because it starts withthe procedures. We think of programming in this way because that ishow programming has been done for 40 years.

Procedural programming, does not always deliver a solution thatresembles the problem, however, because it emphasizes the functionsrather than the data-the procedures rather than the objects. Furthermore,procedural programming does not encourage the programmer to separateand hide the procedures related to different data objects from one another.Programmers have long known that those practices are worthwhile, butmost procedural programming languages do not encourage them.

Object-Oriented ProgrammingThe world and its applications are not organized into values and proce-dures separate from one another. Problem solvers in other crafts do not

Page 27: C++ Database Development 1558283579

1 2 C++ Database Development

perceive the world that way. They deal with their problem domains byconcentrating on the objects and letting the characteristics of thoseobjects determine the procedures to apply to them. To build a house,grow a tomato, or repair a carburetor, first you think about the objectand its purpose and behavior. Then you select your tools and procedures.The solution fits the problem.

The world is, therefore, object-oriented, and the object-oriented pro-gramming paradigm expresses computer programs in ways that model howpeople perceive the world. Because programmers are people, it is only natur-al that our approach to the work of the world reflects our view of the worlditself. We have not, however, universally learned how to do that. The craftsof carpentry, farming, and mechanics are centuries old and originated in cul-tures that lacked technology. The objects and objectives of those trades wereunderstood long before the development of the technologies that supportthem. Computer programming is relatively new. We are still forming its dis-ciplines, and the technologies are developing faster than we can adjust.

The Object-Oriented ProgramAn object-oriented program has four fundamental characteristics:

+ Abstraction defines new data types.

* Encapsulation designs a data type's representation and itsbehavior in one encapsulated entity.

* Inheritance derives a new data type from an existing one.

* Polymorphism customizes the behavior of a derived data type.

Object-oriented programming uses a vocabulary of such terms in waysthat are unfamiliar to the procedural programmer. You hear these termsused frequently in discussions of object-oriented programming. Here is amore comprehensive list of object-oriented terms:

abstract base classabstract data type

abstractionbase class

class

Page 28: C++ Database Development 1558283579

Chapter 2: An Introduction to Object-Oriented Programming 1 3

derived classencapsulationimplementation and interfaceinheritanceinstantiate

messagemethodmultiple inheritanceobjectpolymorphismsubclasssuperclass

The program defines abstract data types in classes, each of which has itsown implementation and interface. Inherited abstract data types arederived subclasses of base classes. The program instantiates an object andsends messages to the object by using the object's methods.

The object-oriented programming community uses these terms univer-sally. Therefore, when object-oriented programmers say "encapsulate", forexample, you know that their meaning is consistent with the way othersuse it. When programmers acknowledge that a program is object-oriented,you know that the program contains abstraction, encapsulation, inheri-tance, and polymorphism and that it defines abstract data types, instanti-ates objects, and sends messages to the object's methods. The discussionthat follows draws on your experience with C++ to explain these terms.

The ObjectThe first question that programmers often ask is, "What are the objectsin object-oriented programming?" What is it about object-oriented pro-gramming that sets it apart from traditional programming? Early writingson the subject effectively explained object-oriented programming to thosewho already understood it, but these explanations were sometimes tooabstruse for the newcomers.

Simply stated, an object is an instance of a data type. The program inFigure 2.1 declares two objects. The first object is a simple integer; thesecond object is an abstract data type.

Page 29: C++ Database Development 1558283579

14 C++ Database Development

FIGURE 2.1 A program with two objects

AbstractionAbstraction is the definition of an abstract data type. The definitionincludes the data representation and the data type's behavior. An abstractdata type is new. It is not one of the primitive data types that are built intothe programming language. The int, long, and float data types are primitiveC++ data types. Their data representations and behavior are known to thecompiler. A primitive data type's format and response to arithmetic, assign-ment, and relational operators are defined as a part of the C++ language.

An abstract data type, however, is not known to the language; the pro-grammer defines its format and behavior by defining a C++ class. For exam-ple, the calendar date could be an abstract data type. The compiler and thecomputer do not know about calendar dates. Programmers have always hadto define the behavior of dates by designing structures and functions. TheStandard C library has several such date definitions. When you define a C++calendar date class such as the one in Figure 2.2, you express its format andbehavior very much as you did in C. It has month, day, and year data mem-bers. It might even support some arithmetic and relational operations.

FIGURE 2.2 An abstract data type (ADT)

Page 30: C++ Database Development 1558283579

Chapter 2: An Introduction to Object-Oriented Programming 15

You can then declare an object that has the type of the date class and addto the object, subtract from it, compare it with other objects, and assignvalues to it. A C++ program declares instances of abstract data types inthe same way that it declares instances of primitive data types. Referagain to Figure 2.1. The declaration of the cdt object is an instance of anabstract data type.

The ANSI C++ standard will define a string class to implement astring abstract data type similar to the strings of the Basic programminglanguage. Other abstract data types will be container classes for stacks,lists, trees, and queues, perhaps with ordered and random-access variantsof such containers.

Instantiate means to declare an object. When an object-oriented pro-gram declares an instance of a data type, the program has instantiated anobject, whether the data type is primitive or abstract. The program inFigure 2.1 instantiates the ndays and cdt objects.

EncapsulationEncapsulation is the design of a C++ class. A programmer encapsulatesthe data representation and behavior of an abstract data type into a classdefinition, thereby giving it its own implementation and interface. Figure2.3 is an example of an encapsulated class.

The implementation of a class, which consists of the data membersand private member functions, is hidden from the using program. Themonth, day, and year data members in Figure 2.3 are the class's imple-mentation. A user of the class does not care about the details of imple-mentation-only that it works. The class designer could totally changethe implementation-perhaps changing the date representation to along integer count of days-and the using program would not beaffected.

The interface, which is visible to the user of the class, consists of thepublic member functions. The class user reads and modifies values inthe data representation by calling public member functions. The classinterface in Figure 2.3 consists of the constructor, which contains ini-tialization parameters, and an overloaded plus operator, which presum-ably allows the user of the class to add an integral number of days tothe date.

Page 31: C++ Database Development 1558283579

16 c++ Database Development

FIGURE 2.3 Encapsulation

To use the abstract data type defined in Figure 2.3, a programmermakes assumptions about the interface based on its appearance andthe programmer's understanding of C++ syntax. If the programmer isnot the class's author, those assumptions may be invalid. The authormight not have designed an intuitive interface. In this case, most pro-grammers would assume that the overloaded plus operator adds aninteger to a Date object and returns another Date object with theresult. Figure 2.4 illustrates how the class interface works if it isintuitive.

void f(

Date dt(6,29,92);dt - dt + 30; // should now~ be 7/29/92

FIGURE 2.4 An intuitive class interface

Designing an intuitive class interface is not automatic. The C++ languageprovides the tools for you to do a proper job, but nothing in the languageenforces good design. The class author can use unnecessarily clever tech-niques that obscure the meaning of the interface. An object-orienteddesign is not necessarily a good design. Experienced C++ programmersgenerally agree on certain design standards for C++ classes. The data

class Date t//-- the class implementationprivate:

int month, day, year;public:1/- -- the class interface

Date(int mo. int da, int yr);Date operator+(int n);

Page 32: C++ Database Development 1558283579

Chapter 2: An Introduction to Object-Oriented Programming 17

members are usually private and constitute most of the implementation.The interface consists only of member functions that restrict access to thedata members. The interface is generic in that it is not bound to any par-ticular implementation. The class author should be able to change theimplementation without affecting the using programs. These apparentrules are only guidelines, however. Sometimes you need to step aroundthem to achieve some purpose. The fewer times you do that, the strongeryour design.

Methods and MessagesMethod is another name for C++ public member functions. Methods maybe constructors, destructors, procedures, and overloaded operators. Theydefine the class interface. The constructor and overloaded plus operatorin Figure 2.3 are methods.

A message is the invocation of a method, which, in C++, is the samething as calling the public member function. The program sends a mes-sage to an object telling it to invoke the method and sometimes providesparameters for the method to use.

Different kinds of methods are characterized by how they support theclass definition. There are functional methods, data type methods, andimplicit conversion methods. Note that this delineation and these termsare coined here for convenience and are not part of the official object-ori-ented lexicon. They define different levels of support that C++ providesfor class design.

Functional MethodsFigure 2.5 shows three methods added to the Date class. The three meth-ods in Figure 2.5 illustrate three typical method variants. The firstmethod tells the object to do something, in this case to display itself. Theclass's implementation knows how to display the object's data representa-tion. The programmer who uses the class is unconcerned about thedetails of the implementation.

The second method tells the object to change itself, in this case toadjust its month data value up or down the number that is specified inthe method's parameter. This method is an example of one that includes a

Page 33: C++ Database Development 1558283579

18 c++ Database Development

parameter. Once again, the using programmer does not care how theobject stores the month value or what algorithm adjusts the month-onlythat it works.

FIGURE 2.5 Functional methods

The third method is one that returns a value-in this case the day of theweek-represented by the object's current value.

The methods in Figure 2.5 define behavior related to the functionalproperties of the class. The class is a calendar date, and you want it tobehave like a date.

Data Type MethodsData type methods make a class act like a primitive data type by giving itthe properties of a primitive data type. These properties are usuallyimplemented as overloaded operators in the class interface. Figure 2.6shows the methods to compare dates, assign them to one another, and dosome arithmetic on them.

Implicit Conversion MethodsThe C++ language handles implicit conversion of primitive data types. Ifyou write an expression with an int where the compiler expects a long,the compiler knows how to make the conversion and does so quietly. If,however, you write an expression where the compiler expects an abstractdata type and if you provide a different data type, primitive or abstract,

Page 34: C++ Database Development 1558283579

Chapter 2: An Introduction to Object-Oriented Programming 19

the compiler does not know how to deal with that. Similarly, if theexpression expects a primitive data type and you use an abstract datatype, the compiler does not know what to do. In either case, unless youhave provided implicit conversion methods, the compiler issues an errormessage and refuses to compile the program.

FIGURE 2.6 Data type methods

You can add implicit conversion methods to a class so that the compilerknows how to convert from one data type to another. The conversionconstructor function converts a data type to an abstract data type, andthe member conversion function converts an abstract data type to a prim-itive C++ data type. When you code an expression in which the compilerexpects to see something other than what you provide as just described,the compiler executes one of your implicit conversion methods to convertthe data type. This is an example of a method that you do not explicitlycall (or send a message to) from within your program. It executes as theresult of the implicit call inferred by the compiler during its interpretationof your expression. Your program implies a call to the method by thecontext in which it uses data types in an expression.

Figure 2.7 shows two implicit conversion methods added to the Dateclass. The first method converts an int data type to a Date object. The

class Date f

public:

tl ---- arithmetic operatorsDate operator+(int n);Date operator-(int n);int operator-(Date &dt);// ---- assignment operatorsDate& operator-(Date &dt):// ---- relational operatorsint operator-(Date &dt);int operatorl,(Date &dt);int operator<(Date &dt);int operator>(Date &dt);

};

Page 35: C++ Database Development 1558283579

20 C++ Database Development

second method converts a Date data type to an int object. The conver-sions are not restricted to converting between abstract data types andprimitive data types. You can convert between abstract data types in thesame manner. For example, you might have a Date class and aJulianDate class, and the same principles apply.

FIGURE 2.7 Implicit conversion methods

Member FunctionsHaving just learned about methods and messages in detail, you soon findthat the terms themselves are not used much in C++ circles. The termscome from the object-oriented lexicon and reflect the syntax of pureobject-oriented programming languages such as Smalltalk. Most C++programmers prefer to say that they call member functions rather thansaying that they send messages through methods. Nonetheless, youshould understand the analogy, because you will read and hear theobject-oriented terms frequently. Beyond this chapter, however, this bookuses the C++ conventions and refers to methods and their messages asclass member functions.

InheritanceA class can inherit the characteristics of another class. The original classis called the base class and the new class is called the derived class. Theseclasses are also called the superclass and the subclass. The word subclassis also used as a verb to mean the act of inheriting. This book refers tobase classes and derived classes exclusively.

Page 36: C++ Database Development 1558283579

Chapter 2: An Introduction to Object-Oriented Programming 21

The derived class inherits the data representation and behavior of thebase class except where the derived class modifies the behavior by over-loading member functions. The derived class adds behavior that is uniqueto its own purpose.

A program can instantiate objects of a base class as well as those of aderived class. If the base class is an abstract base class-one that existsonly to be derived from-the program may not instantiate objects of thebase class.

Inheritance is the foundation of most object-oriented designs, and it isoften over-applied. Some programmers get carried away with the powerof inheritance, and C++ can offer some surprises to the unwary designer.Many designs use inheritance to solve problems that would be better sup-ported by a different approach, usually an object member of the baseclass in what is otherwise the derived class. Despite this warning, inheri-tance is a powerful feature which, properly used, offers a rich designcapability to the object-oriented programmer.

Single InheritanceThe C++ inheritance mechanism lets you build an orderly hierarchy ofclasses. When several of your abstract data types have characteristics incommon, you can design their commonalities into a single base class andseparate their unique characteristics into unique derived classes. That isthe purpose of inheritance.

For example, a personnel system maintains information aboutemployees. Employees have common characteristics-name, address,date of birth, and so on-yet the system might record different kindsof employees. Managers, project workers, and support personnelmight be recorded differently. Therefore, you could design a base classof employees that stores name, address, social security number, anddate of birth and then derive separate classes for managers, projectworkers, and support personnel. Each of the derived classes wouldinherit the characteristics of the employee class and would have addi-tional characteristics specific to themselves. For example, the managerclass might include an annual salary-augmenting bonus data memberthat the other employee classes do not have. The project worker classcould have a list of project assignments. The support personnel classcould record overtime hours worked. Figure 2.8 illustrates such aclass hierarchy.

Page 37: C++ Database Development 1558283579

22 C++ Database Development

FIGURE 2.8 An inherited class design

Multiple InheritanceA derived class can inherit the characteristics of more than one base class.This technique is called multiple inheritance. Not all object-oriented pro-gramming languages support multiple inheritance, and many expertsassert that it is not a necessary design tool. Nonetheless, C++ does sup-port multiple inheritance, and there are times when it is a good way toexpress class relationships.

Recall that the effectiveness of a programming language can be mea-sured in its ability to model the problem domains that it supports. Theobjects in the world reflect membership in multiple-inheritance hierar-chies, and programmers are called upon to write programs to modelthose objects. A sofa bed is a sofa and it is a bed, both of which are itemsof furniture. An amphibious airplane is a boat and an airplane, both ofwhich are vehicles. A car phone is not a car and a telephone, however,and sometimes programmers get their designs confused by thinking thatthey need to inherit when they do not.

Suppose that the system supported by the class design in Figure 2.8needed to record the support personnel class as union members. The sup-port person class object is an employee, and now it is a union member,

Page 38: C++ Database Development 1558283579

Chapter 2: An Introduction to Object-Oriented Programming 23

too. The other employees are not in the union, so their classes do notneed to inherit the characteristics of the union member class.

Figure 2.9 shows how you would derive the support personnel classfrom the union member class as well as the employee class by using mul-tiple inheritance.

FIGURE 2.9 Multiple inheritance

Class RelationshipsObject-oriented designers use the properties of class design to model therelationships between objects of different classes. There are three fundamen-tal interclass relationships: specialization, composition, and collaboration.

SpecializationInheritance models relationships in which a derived class representsobjects that are a kind of the base class. A car is a kind of vehicle. Anengineer is a kind of employee, which is a kind of person. This specializa-tion relationship is also called the IS-A relationship. The derived class is aspecialized version of the base class.

Page 39: C++ Database Development 1558283579

24 C++ Database Development

CompositionInheritance is inappropriate for composition relationships in which oneclass embeds an object of another. An employee has a date of birth. Adepartment has a manager. The owning class is composed of other classobjects. This relationship is also called the HAS-A relationship. Instead ofusing inheritance, the class that has an object of another class embeds theobject-or a reference or pointer to it-as a data member.

CollaborationWhen objects of one class use the services of objects of other classes, a rela-tionship of collaboration exists. This relationship is also called the USES-Arelationship. It can be represented in class design in several ways, and ineach of these ways the objects exchange messages. A class's member func-tions can communicate with other objects through global instances, refer-ences, or pointers. The reference or pointer can be a data member of thesending class, an idiom that resembles the composition relationship butexists for a different purpose. A member function of one class can instanti-ate an object of another class and send messages to the object.

Management

A fourth class relationship, one that has not been named by the establish-ment, exists when one class manages instances of another. For example,container classes manage generic types within defined data structures.The particulars of the type are unrelated to its containment, and the par-ticulars of the containment are unrelated to the types that it contains.

In the past, this relationship was usually implemented with inheritance. Aspecialized class was built that derived from the class being contained and thecontainer class. This gave rise to confusion because the relationship looked likespecialization but did not model reality. Template classes in C++ solved thatproblem. The template implements the container, the type class implements theobject being contained, and the two implementations are independent.

PolymorphismIn polymorphism, a derived class customizes the behavior of the baseclass to meet the requirements of the derived class. A C++ base class

Page 40: C++ Database Development 1558283579

Chapter 2: An Introduction to Object-Oriented Programming 25

uses the virtual member function to specify that overriding memberfunctions in derived classes have polymorphic behavior with respect totheir methods.

If a derived class overrides a base class method and if the base classmethod is not a virtual function, then the overriding behavior is effectiveonly when the compiler is dereferencing a pointer, reference, or object ofthe derived class itself. If the object's pointer or reference refers to thebase class, then the base class method has precedence. Such behavior isnot polymorphic. However, if the base class method is virtual, then thecompiler selects the overriding derived class method regardless of the typebeing dereferenced by the compiler.

Suppose, for example, that the support staff class from Figure 2.8 wasfurther decomposed into derived classes that represented the variouskinds of support personnel. You could have typists, corporate pilots,chauffeurs, maintenance personnel, instructors, and so on. The systemmeasures skill levels differently for each of these disciplines, but there is arequirement for a general-purpose skill index for the base support class.Each derived class exhibits different behavior for data entry and retrievalof the skill index, but some parts of the system invoke the skill methodwithout knowing which kind of support personnel object is involved. Thepolymorphic skill method would modify the class's behavior at run-timebased on the type of the derived class. Figure 2.10 shows how you mightdesign such a class.

FIGURE 2.10 Polymorphism

Page 41: C++ Database Development 1558283579

26 C++ Database Development

SummaryThe object-oriented programming paradigm is a rich environment for theexpression of data formats and functions for an application. It is not nec-essary, however, for C++ programmers to immerse themselves in theobject-oriented passion. The availability of improved design and pro-gramming methods does not automatically outdate all the traditionalapproaches. C++ has the facility to support the basics of object-orientedprogramming while permitting the programmer to use traditional proce-dural programming where it seems appropriate. C++, in fact, encouragesthat approach. By supporting traditional C flow control of nested func-tions, C++ allows you to leverage your existing investment in mature anduseful C function libraries. Furthermore, C++ does not force a pureobject-oriented hierarchical data structure in which every data typedescends from one generic root base object. Instead, C++ allows you tobuild a number of class hierarchies representing the different problemdomains that your application might deal with. There can be classes thatdefine the data structures of the application's functional purpose; therecan be classes that supply general-purpose data structures such as strings,lists, queues, and so on; there can be classes that integrate your applica-tion with a particular user interface. All these classes can coexist indepen-dently of one another in a system of hierarchies integrated by you into anapplication.

ReferencesObjects in Action, Paul Harmon and David A. Taylor, 1993, Addison-WesleyObject Oriented Design with Applications, Grady Booch, 1991, TheBenjamin/Cummings Publishing Company, Inc.

The C++ Programming Language Second Edition, Bjarne Stroustrup,1991, Addison-Wesley

Page 42: C++ Database Development 1558283579

Chapter 3

A Brief History ofDatabase Management

Why is not all nature in confusion, instead of the speciesbeing, as we see them, well defined?

-Charles Darwin

This chapter discusses the progress of database management technologyfrom the earliest ad hoc, hierarchical, and network data models, throughthe enduring relational model and into the newest model, the objectdatabase, which is the subject of this book. We discuss the earlier modelsfor their contribution to the historical perspective and to prepare you forwhat you are about to learn. We must understand our past to manageour present and face our future. You are encouraged to learn more aboutthe earlier database models by reading the references cited at the end ofthe chapter.

The history of database management traces the history of data pro-cessing itself. Ever since we have had information-processing machines,programmers have designed ways to organize data into formats thatmachines can process. Although today we are primarily concerned withfiles organized into direct-access mass storage, the field of database man-

27

Page 43: C++ Database Development 1558283579

28 c++ Database Development

agement began with information recorded in more primitive media, suchas punched cards and paper tape. The common characteristic shared byall database management approaches, from the earliest to the mostrecent, is reflected in this concept:

An entity of data must take a form that the computer canrecognize and deal with.

An atomic entity of data, from the point of view of the database, is adata element. Aggregate entities of data are records. Collections of likerecords are files. To manage files of records, a database management sys-tem applies this rule:

All like records assume a common form.

Given a file of records that adhere to a common form and a descriptionof those records, a database management system adds records to the file,retrieves records from the file, changes records in the file, and deletesrecords from the file.

Records consist of data elements, which are singular entities with val-ues that have functional meaning to the record. Data elements can benames, addresses, dollar amounts, hat sizes, batting averages, dates ofbirth, and so on. A data element may consist of one or more data types ina structure. For example, a calendar date consists of the integers day,month, and year.

A collection of files that support a particular application is adatabase. The information resource of an organization might consist of anumber of independent databases. Some databases interface with others.For example, the payroll database might contribute transactions to thegeneral ledger database.

The software system that manages a database is called, appropriately,a database management system.

The Database Management SystemThe purpose of a database management system is to store, maintain, andretrieve database records in files. A file contains records of the same for-mat that serve a common purpose. A payroll system might have a file ofemployee records, for example, with one record for each employee.

Page 44: C++ Database Development 1558283579

Chapter 3: A Brief History of Database Management 29

Through the database management system, the program adds newemployees when the company hires them, deletes records when employ-ees resign, changes a record when an employee gets a raise, and retrieveseach employee's record once every pay period to calculate paychecks.

A database management system can be custom code or general-pur-pose systems software. Programmers discovered long ago that commondatabase design and programming considerations apply to most applica-tions and that general-purpose database management systems can sup-port these common requirements. The advantage of a general-purposesystem is that developers do not have to rebuild database managementsoftware for each new application. The development team designs anapplication around the capabilities of the database management system.

Use of general-purpose database management systems mandates theapplication of data models. These models have evolved over time, reflect-ing their applicability to a wide range of applications and the inherentstability of databases that use them.

General-purpose database management systems use four languageinterfaces between the application programming language and the data-base manager: the data definition language, the data manipulation lan-guage, the query language, and the report writer language. Depending onthe database management system, these interfaces are built into the pro-gramming language, a separate language processed by a separate lan-guage processor, or some combination of the two.

The Data Definition LanguageThe data definition language (DDL) defines the format-or schema-ofthe database. It identifies files, record formats, and the relationshipsbetween files. The DDL is usually an external file of statements used firstto establish an initial empty copy of a new database and subsequently tospecify the format of the files and records to data manipulation andquery languages.

The Data Manipulation LanguageThe data manipulation language (DML) is the application program inter-face to the database management system. It provides the program interfaceto open and close a database, find records in files, navigate among the var-ious file records, add new records, and change or delete existing records.

Page 45: C++ Database Development 1558283579

30 C++ Database Development

The Query LanguageThe query language describes criteria for searches of the database. Somequery languages are processed by utility programs, and others are imple-mented as embedded extensions to the application's programming lan-guage. A query typically returns a temporary subset of records thatmatch the search criteria. Often the subset has its own format indepen-dent of those in the formal database definition. Sometimes the subsetconsists of records formed from logical combinations of related databaserecords.

The Report Writer LanguageThe report writer language defines reports formed from a query's output.The format specifies the sequence and presentation of data values on thereport, typically with tables of rows and columns, headers, footers, andcontrol breaks for totals and subtotals.

Database ModelsEarly database models were ad hoc. An application used whatever datafiles the designers came up with. If there was a database managementsystem, it was usually a set of custom subroutines developed for theapplication itself. The disadvantages of this practice are apparent, andthey were observed and recorded early on. In fact, they led to the devel-opment of the first general-purpose database management systems.Nonetheless, many systems were-and still are-developed this way fora lot of reasons.

Proprietary Data ModelsMost spreadsheets, word processors, and other horizontal applicationsuse proprietary database formats for three reasons: First, their purveyorsprefer closed architectures that competitors cannot exploit. Second, gen-eral-purpose database management systems impose costs and perfor-mance implications over and above those of the application. The costsoften involve run-time modules that users must have to run the applica-

Page 46: C++ Database Development 1558283579

Chapter 3: A Brief History of Database Management 31

tion. Third, many implementations of the three traditional database mod-els-discussed next-do not support certain data structures (variable-length data elements, lists, queues, and so on) that applications require.

The Hierarchical Data ModelThe hierarchical data model was the first one identified and recognized.Programmers observed that many databases consisted of files with hierar-chical relationships. An organizational database would have files of com-panies, divisions within each company, and departments within each divi-sion, for example. Figure 3.1 illustrates the hierarchical relationships ofsuch a database.

FIGURE 3.1 A hierarchical database

A file higher in the hierarchy is a parent file to the ones below it.Subordinate files contain child records. A child record may have childrenof its own; it can be a child to one level and a parent to another. In thehierarchical data model, a child record type may have only one parentrecord type, and each child record instance may have only one parentrecord instance.

Each department in Figure 3.1 might have its own independent hier-archical database as well. The maintenance department would have a file

Page 47: C++ Database Development 1558283579

32 c++ Database Development

of equipment maintenance schedules. The accounting department wouldhave files for the general ledger, accounts receivable, accounts payable,and payroll.

The natural hierarchical organization of such files indicates that acommon system of software could support all of them if it knew theirformats and interfile relationships. Systems of file descriptions, called theschema, evolved. Programmers defined the database files in the schemalanguage and built their applications programs by linking with a general-purpose database management system.

Hierarchical database management systems support interfile relation-ships by embedding record pointers in the data records. The companyrecord would have the address of the first division record assigned to thecompany. Each division record would have the address of its parent com-pany as well as the address of the next division that belonged to thatcompany. The record addresses were invisible to the applications pro-grams, which would ask the database manager to retrieve the first andsubsequent divisions for a particular company. From a particular divisionrecord, the program could ask for the first and subsequent departmentrecords, and so on.

The Network Data ModelThe hierarchical data model does not support child records having morethan one parent. If the organization supported by the design in Figure 3.1reorganizes so that one accounting department supports several divisions,the hierarchical data model breaks down. The network data modelevolved to support such requirements. Figure 3.2 shows the organiza-tion's database reorganized into a network.

Network databases are typically implemented with record addresspointers like those of the hierarchical data model except that more thanone parent record can point to a particular child record, and the childrecord points to a list of its parents.

In 1969, the Conference on Data Systems Languages (CODASYL)formalized the definition of network databases with the development of astandard for a data description language. A database management systemthat uses the CODASYL standard is said to implement a CODASYLdatabase. The language itself resembles the data division statements ofthe COBOL programming language, and it organizes records into namedsets that consist of owner records and member records. A set resembles a

Page 48: C++ Database Development 1558283579

Chapter 3: A Brief History of Database Management 33

hierarchy in that a parent can have many children, and each child canhave only one parent. Sets can overlap with respect to the records includ-ed in them, however, and a child record can have multiple parents if theparents are in different sets.

FIGURE 3.2 A network database

The Relational Data ModelThe relational data model evolved in response to concern that, due tohidden pointers in the data records, the hierarchical and network modelswere vulnerable to the compromise of data integrity and, therefore, inher-ently unstable. System errors could damage record address chains.Functional relationships between records were compromised by brokenphysical relationships.

The solution to this concern was the relational data model, whereinthe relationships between files are represented by data values rather thanrecord addresses. This does not fully solve the problems of data disin-tegrity. It is possible for a system error to cause an employee record topoint to a nonexisting department, for example. However, the process oferror identification and correction is simpler when user-viewable datavalues are involved rather than system-managed hidden pointers.

Page 49: C++ Database Development 1558283579

34 c++ Database Development

Files in a relational database consist of fixed-length, fixed-formatrecords, which form tables of rows and columns. Each file record has aprimary key data element to uniquely identify the record. This require-ment models the way systems traditionally identify things. People haveSocial Security numbers or employee numbers. Spare parts have partnumbers and stock numbers. Virtually every object has at least one pieceof information that sets it apart from other objects of the same type.

A record in a relational database file is flat in that it has no arrays-lists of common data elements-especially none with a variable numberof entries.

File relationships in the relational model are maintained by data itemvalues. For example, a department record contains the division numberof the division that it belongs to. The division number in the divisionrecord is its primary key. In the department record, the division numberis a secondary key, which means that the application can retrieve a set ofall department records that have a given division number. Figure 3.3shows a typical relational database.

DIVISION

DV DIVISION NAME

DEPARTMENT

DEPT # DEPARTMENT NAME DIV #

FIGURE 3.3 A relational database

The arrow icon that links the two record definitions in Figure 3.3 is not aphysical pointer. Instead, it represents the relationship implied by thedivision number in the department record. Inasmuch as the division num-ber is also the primary key of the division record, several departmentrecords can be related to a single division record, and only one divisionrecord can be related to any department record. This is called a one-to-many relationship and is indicated by the single and double arrowheadsat the two ends of the arrow icon in Figure 3.3.

The one-to-many relationship between divisions and departmentsdoes not support the problem in which one department can be assigned

Page 50: C++ Database Development 1558283579

Chapter 3: A Brief History of Database Management 35

to multiple divisions, however. The relational data model uses a connec-tor file such as the one shown in Figure 3.4 to support the many-to-manyrelationship between data files.

DIVISION DEPARTMENT

DIV # DIVISION NAME DEPT # DEPARTMENT NAME I

DIV # DEPT # LIAISON STATUS RPT

CONNECTOR

FIGURE 3.4 The many-to-many relationship

The concatenated division and department number data elements formthe primary key to the connector file in Figure 3.4. Either number byitself also forms a secondary key. The application can thus find all thedivision records related to a given department record and all thedepartment records related to a given division record. It might evenidentify data items that are unique to the connection itself and putthem into the connector record as well. For example, the connectorrecord might store the employee number of the division's liaison in thedepartment and the date of the department's next scheduled statusreport to the division.

It has been shown that any interfile data relationships that you canrepresent with the hierarchical or the network model can be representedby the relational model through a process called normalization. Thisprocess examines a database's file relationships and reorganizes the dataelements in several steps designed to eliminate redundancy, internal filepointers, and repeating groups (lists) of data elements in the records.Proponents of the relational model assert that the resulting gain in datastability far outweighs any overhead that the approach might add to thedatabase.

The relational model has become the standard method for represent-ing data in a database. The reasons are the inherent strength and stabilityof the model, the fact that the entire data file format is visible to the user,

Page 51: C++ Database Development 1558283579

36 C++ Database Development

and the relative ease with which a general-purpose relational databasemanagement system can be designed and developed when compared tothe effort required to build, for example, a CODASYL database manager.

The Object Data ModelThe object data model is in a state of flux. Members of the object-orient-ed programming and database management industries are working onstandard definitions, and that work is still in progress.

In 1993, the Object Database Management Group published itsODMG-93 draft standard for object databases, which includes an objectmodel, an object definition language, an object query language, and bind-ings for C++ and Smalltalk. The group has formally proposed changes toC++ to support its work, and those proposals are under consideration bythe ANSI X3J16 committee. To date, no database product fully complieswith ODMG-93 as it has been proposed, but the group has among itsmembers several prominent object database management system vendors,and implementations eventually will be available.

Object-oriented programming does not, by itself, change the tradi-tional requirements for database management. A developer still identifiesdata elements and organizes them into files for retrieval and mainte-nance. Object-oriented programming adds to the culture ways to repre-sent user-defined and class library data types with abstraction, inheri-tance, and polymorphism. The object-oriented designer searches for data-base management tools that circumscribe object-oriented class design.

An object-oriented program can use traditional database managementtechniques. To build or modify a database record, the program can col-lect an object's data members into a structure and write the structure tothe database. To rebuild an object, the application can retrieve the struc-ture from the database management system and distribute its data mem-bers into the data members of an object instantiated to receive the data.

Before committing to such a strategy, however, it is proper to considerwhat there is about an object-oriented design that lures the designer awayfrom traditional relational databases.

DATA TYPESThe relational data model uses flat files of fixed-length, fixed-formatrecords. There are no variable-length data items, no repeating groups,and no user-defined data types within the record's definition.

Page 52: C++ Database Development 1558283579

Chapter 3: A Brief History of Database Management 37

These restrictions move us to look for an alternative to the relationaldatabase, but the last one-the absence of user-defined data types-isparticularly compelling. Data abstraction is the strength of object-orient-ed programming. An object-oriented application applies user-defined datatypes because they solve problems of the domain. If the database manage-ment system does not support user-defined data types, the programmerhas to intercede between the parts of the design that define classes andthe parts that manage the database.

If a relational database management system does not support vari-able-length objects and lists, applications suffer. Many applications usevariable sizes and numbers of objects. CAD/CAM programs store digi-tal renderings of engineering drawings. Cartographic systems store vari-able-length lists of geographic coordinates, feature renditions, and free-form map legends. Imagery systems store the compressed raster scans ofdigitized images. Multimedia audio and video data streams have widelyvarying lengths. Word processors use data streams to record docu-ments, macros, style sheets, and graphics. Electronic mail systems storevariable-length files of text and attachments. Simulations can consist ofanything at all. The relational database has limited applicability inthese applications; it was never intended for them. Until the objectmodel was developed, such data types were not supported by general-purpose database management systems, and programmers turned tocustom, ad hoc solutions.

CLASS AND DATABASE DESIGN NOTATION

A natural tendency exists to confuse the hierarchical relationships amongdatabase files with those among classes in an object-oriented design. Thistendency is reinforced by the organizational similarities of the two. In thedatabase, the employee is subordinate to the department. In the classdesign, the engineer is derived from the employee. The difference is subtleand can be obscured by the way that we represent the relationships in ourdesign notation.

Think of the differences this way: an engineer is an employee. Theclass that defines the engineer inherits all the characteristics of the employ-ee class and adds some unique ones of its own. The employee, however, isnot a department. The department employs the employee instead, and theemployee does not inherit any of the department's behavior.

When you design a class to be a data record, you design the data file'srecord format. The format includes all the data elements in the classincluding the data elements that it inherits from its base class.

Page 53: C++ Database Development 1558283579

38 C++ Database Development

When you design a child record, you design the relationship betweenthe record and its parent. The two records might have only one data ele-ment-the primary key of the parent record-in common.

To distinguish these two kinds of relationships, this book uses twodistinct design notations, as shown in Figure 3.5.

DIVISION

Database Design Notation

Class Design Notation

FIGURE 3.5 Class and database design notations

Object-oriented design texts often use a common symbol to representclasses, and they use different pointer notation to distinguish between IS-A (an engineer is an employee), HAS-A (an employee has a department),and USES-A (a payroll paycheck uses a name from an employee). Thesetexts usually address the design of object-oriented applications and donot encounter the collisions that occur between the notation of classdesign and that of a database design.

There are interclass relationships beyond the simple IS-A and HAS-Atypes, and the succinct object-oriented design notations of other texts pro-vide for them. The examples in this book do not use complex class design,however, being primarily aimed at presenting and explaining the PARODY

Page 54: C++ Database Development 1558283579

Chapter 3: A Brief History of Database Management 39

database manager and its use. For purposes of clarity, therefore, this bookadopts the more visually distinct notation shown in Figure 3.5.

SummaryThis chapter discussed the evolution of database management technologyand presented an overview of what an object database is. Implementingsuch a database, particularly as a general-purpose class library to supportthe persistence of as-yet-undefined class designs, is no trivial task.Chapter 4 compares the traditional techniques for storing C structures tothose for storing C++ classes and describes some of the problems of acommon, general-purpose object database manager.

ReferencesThe Object Database Standard: ODMG-93, R.G.G. Cattrell, 1994,Morgan Kaufmann PublishersA Guide to the SQL Standard, C.J. Date, 1987, Addison-WesleyAn Introduction to Database Systems, Second Edition, C.J. Date, 1977,Addison-WesleyComputer Data-Base Organization, Second Edition, James Martin, 1977,Prentice-Hall

Page 55: C++ Database Development 1558283579

Chapter 4

C++ and Persistence...persistence covers every thing from writing an objectout to disk and getting it back to having a full-blown dis-tributed object-oriented database system with concurren-cy control, transaction logging, etc., for multiple users.

-Blarne Stroustrup

This chapter discusses how C++ programs store data structures on diskand how those traditional techniques do not always work with C++objects in an object-oriented design. The problems cause programmers torethink file and database management. Ultimately those problems influ-ence any design in which object persistence is a concern, and they are theprimary motivation for developing object database managers such asPARODY.

File Input/Output in (Reading and writing data records in C is a relatively simple matter. Youdefine a structure, put some data into it, and write the structure to a diskfile. A generic file input/output function can read and write the structure

41

Page 56: C++ Database Development 1558283579

42 c++ Database Development

by using the sizeof operator. This method adjusts to modifications thatchange the structure. If you need features beyond simple recordinput/output, you can use a database management system, but, eitherway, you use flat record structures. The C solution looks like Figure 4.1.

FIGURE 4.1 Persistence in (

The C solution is not perfect. If the structure contains a pointer, thegeneric file input/output function becomes somewhat less generic. Ithas to know what the pointer points to, the length of that object,whether the pointer points to a single object or an array, and, if thepointer points to array, the size of each element and how many thereare. If the pointer points to another pointer, the problem is compound-ed. Except for these restrictions, file input/output in C is a straightfor-ward procedure.

File Input/Output in C++Does the absence of consequential problems mean that C is a better lan-guage for database management than C++? No, it does not, because C'sability to support straightforward record input/output reflects its limita-tions in the expression of data records. You can read and write in C whatyou can express in C.

C++ is a richer environment for expressing user-defined data types. Itsbreadth has consequences that influence how you store and retrieveobjects. C++ introduces a new set of problems related to what you canput in a class.

The features of C++ suggest a solution to the problems they pose.Inheritance is a likely mechanism for adding a persistent property to anobject. Consider a Persistent base class that manages object input andoutput for derived classes. With such a base class, you could derive aclass from it in the manner shown in Figure 4.2.

Page 57: C++ Database Development 1558283579

Chapter 4: C++ and Persistence 43

FIGURE 4.2 A derived Persistent class

The would-be persistent Employee class in Figure 4.2 has several datamembers:

+ an embedded instance of another class, known to the application.

+ a pointer to an object of the standard string class.+ a reference to an object of one of the application's classes.

+ a count of the number of members in an array.

+ a pointer to the first member of an array.

To complicate matters, the class has a virtual function, which means thatthere is a vptr (virtual function table pointer) somewhere among theother data members. Furthermore, you do not know whether the embed-ded objects themselves have vptrs.

The Problem with C++Persistent ObjectsHow would you design a Persistent base class that would know how tofind all the pertinent data members and write them to disk? How wouldthe class know how to construct those data members properly to readthem back in? How would it know the size and location of the data mem-bers? How could it possibly understand the application-dependent rela-tionship between the promotionctr member and the promotions mem-ber? How would it know to get around the one or more vptrs in the class?

class EmploQyee : public Persistent fEiuployeeNo emplno;string *name;Department& dept;int promotion-ctr;Date *PromoQtions;

public:virtual int SalaryRevlewPeriodo;

Page 58: C++ Database Development 1558283579

44 c++ Database Development

The answer is, it would not, and you could not design such aPersistent base class. The following paragraphs examine each of theseproblems in detail.

The Size of the ObjectAssuming that the object's format is a flat structure of data members withnone of the attendant problem members such as pointers, references, andso on, you might consider a Persistent base class that begins like the onein Figure 4.3.

FIGURE 4.3 A flowed Persistent base class

In theory, a class that wants to be persistent would derive itself from thePersistent base class, as shown in Figure 4.3. The base constructor anddestructor functions would read and write the object.

Can you see the flaws in this scheme?The constructor for a base class executes before the constructor for

the derived class. Therefore, when the base constructor executes, noEmployee object yet exists, except for the part that the Employee classinherits from the Persistent class. None of the derived class's membershas been initialized. None of what its constructor contributes to its con-struction has been done. Assuming that the object would eventuallysomehow identify itself well enough for the Persistent class to find itsdisk record address, none of that work is done yet.

Page 59: C++ Database Development 1558283579

Chapter 4: C++ and Persistence 45

Conversely, a base destructor does not execute until the deriveddestructor has completed. The Persistent destructor executes after theEmployee object's destructor has destroyed the Employee data members.There might be nothing left to save to disk. The data values may or maynot have survived in the object's allocated memory. Everything woulddepend on what the derived destructor does.

The solution shown in Figure 4.3 will rarely work, if it works at all.The program must wait until the object is fully constructed before it canread the data members, and it has to write them before they aredestroyed. Figure 4.3 is a failed design. The base class in Figure 4.4attempts to correct these flaws.

class Persistent ffstream objiflie;

public:void ReadC)

( objfile.read(tIhis, sizeof(*this)); IIvoid Write()

t objfile.write(this, sizeof(*this));1

FIGURE 4.4 Improved but flawed Persistent base class

The Persistent base class in Figure 4.4 adds Read and Write memberfunctions that can be called after the object is constructed and before it isdestroyed. Apparently, the derived class and/or its user must participatein the object's persistence. Merely inheriting the behavior of a Persistentbase class is not enough. This is an important discovery. It figures promi-nently in later chapters.

Whether the derived class calls Read and Write from its own construc-tor and destructor or the using program calls them while the object is inscope is irrelevant. The base class still does not work. Can you see why?

The base class is using the sizeof operator to specify the size of theobject. The sizeof operator is not polymorphic. When you apply sizeof toa base class, it returns the size of an object of the base class, whether ornot the object is a derived class. To solve that problem, the Persistentclass levies another responsibility on the derived class-the derived classmust supply its own size. Figure 4.5 shows that modification in place.

Page 60: C++ Database Development 1558283579

46 c++ Database Development

FIGURE 4.5 A Persistent base class that almost works

The Persistent base class in Figure 4.5 almost works. The constructor forthe Employee class provides the size of its own object in the call to theconstructor to the Persistent class. From that value, the Persistent classnow knows the size of the object, and it can read and write the correctsize. If you can assume that the problems yet to be discussed cannotoccur or that they are not allowed in a derived class, then the base class iscapable of reading and writing the data members of objects of thederived class. There are two small bugs, however. Can you find them?

Note: This discussion has avoided-for now-how to position thefile's pointers to the object's location. It also defers any discus-sion about how to handle objects whose sizes change while theyare in scope and how to delete objects. These are valid concerns,but their solutions involve no obstacle more serious than the

Page 61: C++ Database Development 1558283579

Chapter 4: C++ and Persistence 47

selection of an appropriate approach. The book addresses thesematters after identifying and resolving more serious problems.

Non-Persistent MembersThe issue of object size is resolved, but the other problems are still ahead.Figure 4.5 hints at the next two problems. By reading and writing theentire size of the object beginning at its address, the program reads overthe objfile data member that defines the stream object and the vptr vari-able that points at the hidden vtbl for the class. More about that later.These bugs will surely cause the system to fail.

You could define the obifile object as static and get it out of theobject's data space. That would solve that particular problem if allPersistent objects used the same file, but the bug is an indication of onesthat your derived classes could have as well.

Many derived classes would have objects that should not be read andwritten. Some data variables deal with the run-time context. There couldbe state variables such as input/output handles or user interface windowhandles that depend on run-time conditions and that must not or shouldnot persist beyond a specific instance of the data type. The Persistent baseclass cannot know about these data members in the derived class andtherefore cannot, without cooperation from the derived class, do any-thing to get around them.

Pointers in the ObjectRefer to Figure 4.2. The hypothetical derived class has pointers. APersistent base class cannot know anything about those pointers. It hasno way of knowing the data types the pointers point to. Even thoughyou, the programmer, can look straight at that class definition and seeclearly what the pointers are, the base class cannot-particularly at run-time. C++ is not that kind of object-oriented language. Such languagesmight exist, but C++ is not one of them.

Clearly, the base class cannot write and read the pointer values to andfrom the database. The value from one instance of the object is not likelyto persist for a subsequent instance. The pointer would point intounknown territory. The object that it points to might not even have beenconstructed for the subsequent instance. Moreover, the base class cannot

Page 62: C++ Database Development 1558283579

48 c++ Database Development

tell that the data member is a pointer because the base class cannot knowanything at all about the contents of the derived class.

Clearly, something other than the Persistent class has to decide whichdata members to read and write and which ones to skip.

References in the ObjectThe class in Figure 4.2 has a reference to another object. When you see areference to an object within another object, you know that the datamembers for the referenced object are somewhere else. Chances are, thereferenced object has not been constructed at the time the referencingobject gets read in. The problems are similar to those of the pointer butwith one more wrinkle-a reference in an object must be initialized bythe object's constructor declarator block. To instantiate an object that hasa reference in it, you must already have the referenced object in scope. APersistent base class cannot know how to do that. Only the derived classor the application that uses it can know where the referenced object is.

Format-Defining Related ObjectsYou can probably assume that the promotions pointer in the class defini-tion in Figure 4.2 points to an array of objects and that the promotionctrinteger is a count of the number of objects in that array. You can make thatassumption because the members' identifiers and their proximity to oneanother suggest that to be the case. A Persistent class could not infer suchrelationships among the members in the derived class and could not, there-fore, do anything appropriate with them.

Embedded ObjectsAn object that is embedded in the would-be persistent object repeats allthe same problems of the Persistent object except that they are not asobvious at first glance. You have to go to the class design for the embed-ded object. If you designed the embedded object for your application,then you have some control over how much and how well it participatesin its own persistence. Nonetheless, you still have to address all the sameproblems of persistence that the original class has.

Page 63: C++ Database Development 1558283579

Chapter 4: C++ and Persistence 49

Remember, too, that embedded objects can themselves containembedded objects.

Objects from Class LibrariesIf the persistent class embeds, references, or points to objects from classlibraries, the format and design of those classes are probably beyond yourcontrol. You can do little to recruit those classes for participation in theirown persistence, particularly if you have no source code for the classmembers. Even if you have the code, the classes might be under rigid pro-ject management that forbids their modification. (You could contrive per-sistent specializations of those classes through multiple inheritance, butthe resulting design could become too complex to comprehend.)

It would be a shame to have to adopt a persistent object strategy thatdoes not permit you to use other class libraries in your design. A reasonfor choosing C++ in the first place is the vast resource of class librariesthat support specific problem domains. You should not have to forsakesuch a resource just to include persistence.

Virtual FunctionsThe class in Figure 4.2 includes a virtual function. When a class has virtu-al functions, the compiler builds a table of pointers to the functions toaccompany the class. Usually, only one table exists in memory for theclass, and each object of the class has a pointer to the table. These con-ventions might vary between compilers, but the mechanics, and thereforethe attendant problems, are similar to those described here. The pointerin the object is called the vptr, and the table is called the vtbl. When theclass has derived classes and when those derived classes override the vir-tual functions, the compiler puts the overriding function addresses in thevtbl. The C++ language supports polymorphism by calling virtual func-tions through these tables.

The presence of a hidden vptr presents another problem for thePersistent base class and is another reason that the base class shown inFigure 4.5 cannot work some of the time. If the program writes the fullcontents of the object to a disk file, the image includes the vptr. If anoth-er program instantiates the object and if the base class reads its imagefrom the database, the image writes an old, incorrect value over the new

Page 64: C++ Database Development 1558283579

50 C++ Database Development

program's vptr for the object. When the program tries to execute one ofthe virtual functions, the system probably crashes.

There is no way around the vptr problem. You cannot eliminate vir-tual functions. The Persistent base class must have a virtual destructor sothat the derived destructors are executed in cases when destructionoccurs through a reference or pointer to the base class.

Copies of ObjectsA persistent object can exist in two places: in the database and in thememory of the computer when a program instantiates it. While the objectis in memory, the memory copy represents the object. Any changes thatthe program makes to the object reflect the current state of that object,and the disk copy is-at least for the moment-out of date and potential-ly unstable. When the object goes out of scope, the persistent databasemanager returns the memory copy to the disk, and everything returns toa stable condition.

What happens when the program makes two copies of the object?There are several ways that this can happen: The program can instantiatemore than one copy and have both of them in scope at the same time; theprogram can pass an object by value to another function; the programcan assign the object to another object of the same class.

To C++, these multiple copies are different objects, but to the applica-tion, they are the same. Employee 123 is Bobby Pickwood, regardless ofhow many copies of Bobby's employee record are in the computer. Nowsuppose that the program changes the two copies in different ways.Whichever copy of the object goes out of scope last is the one that pre-vails, and that is not necessarily the desired effect.

The situation just described represents a less-than-perfect programdesign, but sometimes it can happen to the unwary. Should the persistentdatabase manager worry about this problem, or should there simply be arule stating that you do not write code that duplicates objects? Theseissues are addressed in later chapters.

SummaryThis chapter discussed the problems associated with storing and retriev-ing objects in a persistent object database. The process is not as easy as it

Page 65: C++ Database Development 1558283579

Chapter 4: C++ and Persistence 51

might appear at first glance, and this chapter explained why. Chapter 5discusses some alternative solutions to those problems and identifies theobjectives of a persistent object database.

ReferencesThe Annotated C++ Reference Manual, Ellis and Stroustrup, 1990,Addison-Wesley

Page 66: C++ Database Development 1558283579

Chapter 5

Solving the PersistentObject

Persistence is the property of an object through which itsexistence transcends time (i.e. the object continues toexist after its creator ceases to exist) and/or space (i.e. theobject's location moves from the address space in which itwas created).

-Grady Booch

Chapter 4 posed several problems related to supporting persistent objectsin a higher-level C++ base class that assumes most of the burden for per-sistence. This chapter looks at alternative solutions to those problemsand identifies the objectives of a proposed solution.

53

Page 67: C++ Database Development 1558283579

54 C++ Database Development

Alternative SolutionsHow can you solve the problems posed in Chapter 4? There are a num-ber of ways to approach the solution. Some of them solve parts of theproblem, and some solve other parts. Some solutions are less than nosolution at all. Among the alternatives are:

* Use custom file input/output methods.

* Extend the C++ language.* Write a C++ preprocessor.* Limit the scope of a persistent class.

* Use a relational database management system.* Let the persistent class participate in its own persistence.

The most frequently used solution, described next, is one of the leastdesirable.

Custom File Input/Output MethodsThis first alternative is nothing more than what many C++ programmershave been using all along. They forgo the idea of a Persistent base classbecause of the difficulties. Instead, they write custom file storage andretrieval methods for the persistent classes in their design. This approach,although widely used, betrays everything that programmers have learnedabout database management in the last 30 years.

A long time ago, programmers discovered the inherent similarities inthe requirements for database management across applications. Fromthat discovery grew disciplined data models and dat base managementsoftware systems that implemented them. These were important discover-ies and developments. Many large-scale software systems exist today onlybecause database management tools existed to make them possible. It isunthinkable that the software industry would not have hit upon what isnow obvious-that the processes of organizing, storing, and retrievingdata records should be managed by reusable, general-purpose systemssoftware.

The apparent persistence-contrary nature of the C++ class definitiondescribed in Chapter 4 is the result of its power. You can design a classwith all the things that are difficult for a database manager to pin down.

Page 68: C++ Database Development 1558283579

Chapter 5: Solving the Persistent Object 55

You want those things in your classes even though they get in the way ofan easy persistence strategy. But that doesn't mean you should abandonthe effort-only that it is going to take some thought and work.

Extend the C++ LanguageThe second alternative is to change the syntax of C++ to include per-sistence. You can't do that on your own, though. C++ is a formal lan-guage undergoing formal definition by the ANSI X3J16 C++ standard-ization committee. You might propose to X3J16 that it add the persis-tent attribute to the language and that it figure out how to make itwork. Perhaps you could develop the syntax and implementationdetails. Assuming that you propose a workable design, it is doubtfulthat you could persuade the committee's Extensions Group (chaired byBjarne Stroustrup) to adopt it. The committee has charted a coursethat standardizes the language in ways that conform with theAnnotated Reference Manual with a few extensions, and many influen-tial experts rightly assert that persistence is not a language issue. Thenature of persistence tends to draw the solution closer to the targetoperating environment. C++ is a portable language, as portable as C ifnot more so. One of the features of both languages is that their com-pilers are written in the languages themselves. If you add input/outputprimitives to the language, the portability of the compiler and of thelanguage is compromised.

There are language features that the X3J16 committee can add toC++, however, that enhance object database management systems. Onesuch feature is run-time type information (RTTI). When I wrote the firstedition of this book, RTTI was under consideration but not yet definedor approved. Now, RTTI is officially a part of the language, and softwaresystems such as PARODY can use it.

The Object Database Management Group has, as part of its ODMG-93 standard, maintained an interest in proposals to the ANSI X3J16committee for extensions to the C++ language that would support objectdatabase management. Two of these proposals (ANSI documentX3J16/92-0048) are:

* Permit overloading the dot (.) operator so that dynamic logi-cal views of object retrievals can be supported.

+ Distinguish between rvalue and Ivalue usage of overloaded dot(.) and subscript ([) operators so that an object database

Page 69: C++ Database Development 1558283579

56 c++ Database Development

manager can automatically sense when a program has writtena change to a memory-resident persistent object and automat-ically write the object to disk.

Write a PreprocessorYou can extend C++ without getting involved with X3J16 by writing apreprocessor program that translates an extended C++ language into cor-rect C++ code. This approach is consistent with the tradition of C++; theoriginal C++ implementation from AT&T translates C++ code into C.Depending on the extent of your translator's support for persistence, thepreprocessor might need to search all the header files included in yourprogram down to the deepest nested level. This search would ferret outthe formats of embedded classes to see how to save and retrieve theirdata members. The preprocessor approach does not solve problems dis-cussed in Chapter 4, such as the relationship between the array elementcounter and the array pointer, but it is a step closer. Some object-orienteddatabase management systems use preprocessors.

Limit the Scope of a Persistent ClassYou can probably get around all the problems mentioned in Chapter 4 bylaying down ground rules for users of your persistent object database sys-tem. You can specify that to derive from the Persistent class, an objectmay not use any of the C++ features that cause those problems. If yourapplication can get by without the features, then such an approach mightwork. However, if limiting a persistent object class to what is easy toimplement suits you, then you might as well use one of the relationalDBMSs that are already available.

Use a Relational DBMSYou can build an object database manager by putting a C++ wrapperaround an existing C library that implements a relational DBMS. That isa viable approach, and there are times when it is the appropriate one.Some so-called persistent object database management systems are simplywrapper classes. Chapters 6 and 10 discuss some of the considerationsfor making this decision.

Page 70: C++ Database Development 1558283579

Chapter 5: Solving the Persistent Object 57

The Persistent Class Participates inIts Own PersistenceThis alternative is the one used by the software in this book. Every prob-lem identified in Chapter 4 points to one undeniable conclusion. Theintelligence that identifies what can and should be persistent in a class iscontained in the class itself. The class must cooperate with the persistentobject database management system. That conclusion is the basis for thePARODY database management software described later in this book.The rest of this chapter is about the objectives for PARODY, thePersistent, Almost Relational Object Database manager in which a classcooperates in its own persistence.

Objectives of a Persistent ObjectDatabaseThe first objective is for an effective method to describe the persistencecooperation of the using class. It must be easy for programmers to use.The database manager must have a simple, intuitive, and consistent inter-face. There should be a minimum number of member functions in theinterface, and they should use C++ language features in an intuitive man-ner. This objective permeates the PARODY project. Following are specificobjectives for how the persistent object database system must work.

Object IdentityWhen a program creates an object, the object's identity is (if nothing else)its location in the program's memory-that is, its address. Certainly theprogram knows where it is. When the program is finished with theobject, the database manager stores the object in a database, and the pro-gram has no further need to identify the object. Subsequently, when theprogram or another program reuses the object, the program providesidentifying information to the database manager to retrieve the object.The original object's memory address is without meaning then. Theremust be something about the object known to the program that the data-base manager can use to retrieve the object from the database.

Page 71: C++ Database Development 1558283579

58 C++ Database Development

There are two ways to access objects in a database-navigational andassociative access-and support for both is an objective of a persistentobject database manager.

NAVIGATIONAL ACCESS

Navigational access assigns object identifications-usually related to theobject's logical address in the database-as the object's identity. Theapplication remembers these addresses in order to retrieve the objects at alater time. Objects related to other objects of other classes use objectidentities to point to their relatives.

When a program creates a persistent object, the object database man-ager reports the object's identity to the program. The application decideshow to use the identity to retrieve the object and relate it to other objectsof the same and other classes.

ASSOCIATIVE ACCESS

Associative access uses key data members to associate objects with theirposition in the database. The application retrieves objects by providingprimary key data member values to search. It relates objects by storingprimary key data members as secondary keys in other classes.

The persistent object database manager maintains index files thatassociate navigational object identities with key member values. When aprogram creates an object with a primary key value, the database manag-er updates the index. When a subsequent program instantiates an objectof the same class and specifies the same key value, the database managerretrieves the associated object. The database manager maintains sec-ondary indexes, too, so that programs can retrieve specific objects on thebasis of data values other than the primary key.

Maintenance of ObjectsThe persistent object database manager includes methods to add, retrieve,change, and delete objects in the database. The database is not merely arepository or cache of stray objects. The application creates objects andspecifies that they are to be persistent. The application retrieves specificpersistent objects that were created earlier, changes their data values, andreturns the updated objects to the database. The application deletes specificobjects from the database. These capabilities have implications with respectto the application-specific integrity of the relationships between objects.

Page 72: C++ Database Development 1558283579

Chapter 5: Solving the Persistent Object 59

OBJECT INTEGRITY

Maintaining object integrity means that the database does not storeambiguous objects and that it does not retrieve objects that have not pre-viously been stored. Ensuring object integrity in an associative accessdatabase is one of the objectives. The persistent object database managerindicates when a requested object is not in the database, and it refuses toadd objects when the addition would collide with-have the same identi-ty as-an existing object.

If the application instantiates an object with a primary key value thatthe database does not recognize, the database manager so indicates.

If the application tries to add an object when one already exists withthe same primary key value, the database manager refuses to add theobject and returns a negative response to the add request.

Maintaining object integrity keeps the database in a stable conditionwith respect to the requirements of the application. There should not betwo department records with the same department number. A programshould not behave as if it has retrieved an employee object when noemployee record exists with the requested employee number.

NAVIGATIONAL CLASS RELATIONSHIPS

In a navigational access database, classes are related to one another bydata members in the class design that emulate C++ reference objects butthat extend the reference to the database. If a class maintains a persistentreference to an object, the program instantiates both objects and assignsthe reference. Both objects must be instances of persistent classes. Whenthe referencing object is written to the database, the reference informa-tion is written along with it. The technique uses navigational object iden-tity-which is hidden from the class user-to form the reference. Whenthe program reinstantiates the referencing object, the database managerautomatically instantiates the referenced object, reads it from the data-base, and forms the reference through the persistent reference data mem-ber in the referencing class. The referencing object can access the refer-enced object through its persistent reference data member.

ASSOCIATIVE CLASS RELATIONSHIPS

In an associative access database, classes are related to one another inways that the application manages. The relationship is important to theapplication. Objects of the employee class are related to objects of thedepartment class where the employees work. In a strictly object-oriented

Page 73: C++ Database Development 1558283579

60 c++ Database Development

program, classes can be related by references or pointers in one class toother classes. This method works as long as the objects are memory-bound. The references and pointers refer to memory addresses. When theobjects are in the database, such addresses are not usable. When otherprograms retrieve the objects, the addresses are meaningless. Someobject-oriented database managers assign logical addresses to the persis-tent objects and an index of those addresses to the physical disk address-es. For those addresses to have meaning to an application, the applicationmust relate them to data values that identify the object.

The persistent object database specified in this chapter maintains rela-tionships between associative access classes on the basis of key data valuesrather than on the basis of pointers, and it maintains the integrity of thoserelationships. If a class includes another class's primary key as a secondarykey, the classes have an implied relationship. The persistent object data-base does not delete an object if other objects that are related to it remain,and it does not attempt to relate an object to one that does not exist.

If the application retrieves an employee object and changes its depart-ment identification-perhaps because of a user's input-to an invaliddepartment, the database manager does not allow the object to bereturned to the database. What is more, the database manager notifiesthe application that the replacement was not allowed. Without these con-trols, a database would have the potential to become chaotic, withrecords implying relationships that could not exist. The database wouldhave lost track of the employee's department assignment. A subsequentapplication program might depend on the presence of department infor-mation for every employee, and that program would fail when itprocessed the errant object.

If the application deletes a department object while there are employ-ee objects related to it, the same kind of integrity violation occurs.Therefore, the object database manager does not allow an application todelete an object if other objects are related to it. You cannot shut down(delete) the department while employees are still working there. You musttransfer (change) or terminate (delete) the employees so that none ofthem are assigned to the department that you want to delete. The datamodel reflects the problem domain that it supports.

Copies of ObjectsIf a program declares multiple memory copies of an object-whetherthrough assignment, copy construction, or multiple instances-the data-

Page 74: C++ Database Development 1558283579

Chapter 5: Solving the Persistent Object 61

base manager must know about it. There are several ways to deal withthat situation. One solution marks all subsequent copies as being read-only. The application can change only the original copy. Another solutionsimply ignores the problem and treats it as a policy matter. Programsshould not make copies of objects in this approach. A third solution,which the software in this book uses, is to apply a C++ idiom called refer-ence counting, in which all such copies are actually references to the orig-inal. Chapter 8 describes how that works.

SummaryThis chapter discussed several alternative solutions to the problems present-ed in Chapter 4. Then it chose one of them as the basis for the PARODYsystem and developed the objectives for that system. Chapter 6 comparesthe object database with the relational data model, shows how the objectmodel benefits from the lessons of relational technology, and discusseswhen you should choose each one.

ReferencesThe Design and Evolution of C++, Bjarne Stroustrup, 1994, Addison-WesleyThe Object Database Standard: ODMG-93, R.G.G. Cattrell, 1994,Morgan Kaufmann PublishersThe Annotated C++ Reference Manual, Ellis and Stroustrup, 1990,Addison-Wesley

Page 75: C++ Database Development 1558283579

Chapter 6

The Object Database andthe Relational Database

The compass and square produce perfect circles andsquares. By the sages, the human relations are perfectlyexhibited.

-Mencius

The object database is a relatively new concept. There are not nearly asmany implementations as there are of the traditional relational database,although that situation is changing.

Some persistent object implementations emulate the in-memorynature of object relationships in a program by writing a disk image thatcontains all of memory. Others put C++ wrapper classes around existingrelational database management systems. Still others use Extern "C"linkage specifications to link with database management functionlibraries. There is still no consensus about what constitutes a genuineobject-oriented database. The work of the Object Database ManagementGroup will eventually provide that definition, but the technology isyoung. We are still learning about its implications, complications, andconsequences.

63

Page 76: C++ Database Development 1558283579

64 c++ Database Development

The relational database is, on the other hand, a time-worn andproven concept. Relational technology evolved in the 1970s to solve theproblems associated with traditional hierarchical and network databases.The relational data model eliminated hidden pointers by forming filerelationships with data values, and it improved the user's perception ofdata representations by making the schema visible to the user and func-tionally relevant to the application.

There are restrictions in the relational data model that discourage itsuse in an object-oriented design. Conversely, it has benefits that encour-age its use. This chapter compares the requirements for an object data-base to the advantages of the relational data model for two purposes:first, to identify the items you should consider when deciding whether touse the object or relational models; second, to identify and justify thosefeatures of the relational model to adopt for the PARODY object model.

The Object Database ModelIf the object model-the basis for object-oriented design-applies theprinciples of abstraction, encapsulation, inheritance, and polymorphism,then the object database model must extend the object model into therealm of database management. Objects, therefore, may be stored in andretrieved from an object storage medium that survives after the object'screator expires. An object stored in such an object database is a persistentobject.

Each persistent object possesses a unique identity. Except for that, theobject database model imposes no requirements or restrictions on thecontent or format of the persistent object. Therefore, if you can defineand instantiate an object of a user-defined data type, you can store thatobject in an object database and retrieve it at a later time.

Until recently there has been no formal definition of how the objectdatabase model should be implemented. Its internal format and architec-ture are still not defined. Its interface with a using program is being stud-ied and developed now. The work is not yet complete. The PARODYobject database implementation in this book does not comply with anystandard, de facto or otherwise, because so far no such standard exists.

To understand why this is so, you must consider the relationshipsbetween traditional programming paradigms and database models. Thereis no relational programming paradigm. There is no structured database

Page 77: C++ Database Development 1558283579

Chapter 6: The Object Database and the Relational Database 65

model. Programmers adopted structured programming because it wassuperior to the ways they had been using to express procedural algo-rithms. Database designers adopted the relational database modelbecause it was superior to the ways with which they had been designingdatabases. The two came together only coincidentally. A structured pro-gram could always use a nonrelational data model. A nonstructured pro-gram was in no way prevented from using the relational data model. Butbecause procedural programming does not encapsulate the design of datarepresentation and data behavior, the two then-superior approaches tosoftware design were never necessarily bound.

The object model is the first attempt to marry a programming modelwith a database model. Why? Object-oriented design expresses dataabstraction in ways that no traditional data model does. Object-orienteddesign needs its own database model.

Inasmuch as there are no restrictions on the format and content of apersistent object, the object data model is able to represent any kind ofdata. There are applications that use streams of data that traditionaldatabase management systems do not manage well. Usually, these datastreams represent long, variable-length, formless bit packages which aresometimes in arrays of variable dimensions.

CAD/CAM systems deal with such objects. They store digital repre-sentations of engineering designs that consist of wire-frame and solidmodels. The models record the properties of their composition to facili-tate finite-element analysis of their reactions to stress, temperature, vibra-tion, and motion. Imagery systems store raster scanned images.Cartography and meteorological systems store points, vectors, and refer-ences to cataloged features. Multimedia systems store bit streams ofaudio and video recordings. Text-processing systems store streams of textwith embedded formatting control fields.

Objects such as the ones just described appear formless to the casualobserver, and so they are not particularly suitable for a database modelthat imposes rigid form. You would have to reform the objects to makethem fit the database.

The Relational Database ModelThe relational model is the result of the work of E.F. Codd in the 1960s.He described a data model in which entities of data are represented by

Page 78: C++ Database Development 1558283579

66 C++ Database Development

relations, which resemble tables of rows and columns. Most program-mers view these relations as files in a database. The rows are the individ-ual file records, and the columns are the fields within the records. Codddoes not define the model to that level, however, leaving the details ofhow to form the relations to the implementor. Codd calls the rows byanother name-tuples.

Figure 6.1 shows a typical, simple relational database with two relations.

DEPARTMENTS

DEPT NO DEPT NAME

00020 SALES00030 ACCOUNTING00040 ENGINEERING00050 MANUFACTURING

EMPLOYEES 6 _-

I

FIGURE 6.1 A relational database

Any discussion of the relational model is incomplete without considera-tion of Codd's 12 rules. Without enumerating them here or presentingthem in their original sequence, this discussion addresses the essence ofthe rules to the extent necessary to compare the features with the objectmodel.

As shown in Figure 6.1, all the rows in a relation have the samecolumns. The data value for a particular row/column may be null, whichmeans that no value has been assigned.

There are no repeating groups-arrays-in a relation. There are novariable-length columns.

Each relation has a column or aggregate of columns that is its prima-ry key data element. Each row must have a unique value in that key todistinguish the row from the other rows in the relation. The EMPLNO

EMPLNO EMPL NAME DATE HIRED SALARY DEPT-NO

00001 JONES 05/06/84 22000 0003000002 SMITH 02/04/83 22000 0002000003 BROWN 09/21/82 21000 0003000004 GREEN 07/12/80 18000 0004000005 WHITE 11/11/85 25000 00020

- I - I - U - U -

EMPLOYEES

Page 79: C++ Database Development 1558283579

Chapter 6: The Object Database and the Relational Database 67

column is the key data element for the EMPLOYEES relation in Figure6.1. The DEPTNO column is the key data element for the DEPART-MENTS relation. A program may not add an EMPLOYEE relation withan EMPLNO column value of 00002, for example, because one alreadyexists.

The application program may retrieve any element of data in thedatabase by specifying the relation, the column name, and the value ofthe relation's key column, which might not be the same column as theone being retrieved. Many implementations simply allow the applicationto retrieve the full record by providing a buffer that holds it and the keyvalue.

The relational database describes its own schema in a file called thecatalog. This catalog allows general-purpose query and report writer lan-guages to interact with the user and the database.

The user can create views of the database from queries. The viewslook like relations but are the result of specifying a different configura-tion of columns with the PROJECT operator, combining the contents ofmultiple relations with the JOIN operator, and using a subset of the pos-sible rows with the SELECT operator. These views are not, theoretically,new relations. If a data value in the database changes, any views that thedata element participates in reflect the change. The user can change aview only when the change would not damage the integrity of otherviews or of the database itself.

The database manager enforces the integrity of the relationships thatexist between relations as specified by rules in the catalog. With the data-base shown in Figure 6.1, a program cannot delete the DEPARTMENTrow that has the 00020 DEPTNO column value because employees arestill assigned. Similarly, a program cannot assign an employee to adepartment that does not exist.

Database administration does not affect the users' operations. If theadministrator reorganizes the physical or logical organization of the data-base, the users are unaffected.

Choosing a Database ModelWhen should you use a relational database and when should you use anobject database? The answer depends on several considerations. Is yourproblem domain already supported by a mature database and database

Page 80: C++ Database Development 1558283579

68 c++ Database Development

management system? If so, you might not want to risk the change.Pioneers, although heroes in history, did not reap the rewards of theirexplorations. Those who followed after the way was made safe foundprofit and success. The same is true in software design. If your design canleverage earlier successful efforts, it stands a better chance for success ofits own.

If you are not building on earlier work, then the answer lies in ananalysis of the mutually exclusive strengths and weaknesses of the twodata models and the requirements of your application. The relationaldata model has advantages that the object data model cannot support.Among these are:

* The relational schema is stored in the database catalog.

+ General-purpose query programs can use the catalog.+ The SELECT, PROJECT, and JOIN operators can build new

database views at run-time.* The database is compatible with other applications that use

the same DBMS.

Conversely, the object data model supports data representations that therules for the relational data model prohibit:

+ Variable-length data members can support such applicationsas imagery, multimedia, geographic data, and weather.

+ Abstract data types support user-defined data abstractions.

+ Arrays permit repeating groups.

+ Encapsulation of data formats with methods binds data repre-sentation and behavior.

+ Polymorphism customizes the behavior of derived data types.

Clearly, the strength of the object data model is that it supports anobject-oriented design. A designer must decide which way to go byweighing the benefits of both approaches. If you decide that you needthose relational features not available in the object model, then use arelational database manager. If your database is organized into well-defined tables of rows of fixed-length columns, the relational databaseis the clear winner. You will find, however, that the object model canemulate some of the behavior of the relational model, and if the parts itomits are expendable, then the object model is a better choice for theC++ program.

Page 81: C++ Database Development 1558283579

Chapter 6: The Object Database and the Relational Database 69

Adapting the Relational Model to ObjectsHaving chosen the object model, one must decide how to implement it.Chapter 5 identified the objectives for the PARODY system of objectdatabase management. There are advantages to the relational model thatthe object model can adopt in support of those objectives. The mostprominent one is the ability to support associative access, and a sec-ondary one is the maintenance of relational integrity among objects. Thediscussion that follows describes in narrative how PARODY emulatessome of the features of a relational database. Later chapters describe thePARODY interface in detail.

The Database SchemaThe relational model includes a data definition language (DDL) in its cat-alog that defines the format of records in the database files and the rela-tionships of files to one another. The DDL is said to define the databaseschema. The PARADY DDL is C++ itself. You design a database bydesigning classes that define the file formats and their key data members.Figure 6.2 illustrates such a design.

FIGURE 6.2 A database schema

Page 82: C++ Database Development 1558283579

70 c++ Database Development

The Employee and Department classes in Figure 6.2 are derived from thePersistent class, which is how they acquire the persistent attribute. You will learnhow the Persistent class works in later chapters. You will learn, too, that classesderived from the Persistent class do specific tasks in their constructors anddestructors and provide a few additional members to support their persistence.

Three data members in the two classes in Figure 6.2 use the Key tem-plate class. This is how you specify primary and secondary key data mem-bers. Later chapters explain how Key classes support the PARODY indexingmechanism. The Employee class has two Key members: the emplno anddeptno objects. The first Key object is the class's primary key. All subsequentKey objects are secondary keys. There can be only one primary key memberin a class, because the primary key value identifies the object. Multipleobjects of the same class can however, share secondary key values. TheDepartment class in Figure 6.2, for example, has a deptno member as itsprimary key, so there may be only one Department object with a depart-ment number of 123. The Employee class has a deptno member as a sec-ondary key. There can be several employees assigned to department number123. This relationship is an implied one based on the presence of those Keydata members. You will learn in Chapter 8 how PARODY enforces impliedrelationships. The design implies the relationship, so the persistent objectdatabase can maintain it. You are not allowed to write an Employee objectwith a non-null deptno value unless there is a corresponding Departmentobject with the same key value. You are not allowed to delete a Departmentobject if any Employee objects include the matching deptno key value.

Retrieving ObjectsA program retrieves a persistent object by declaring an instance of it withits primary key member value as an initializer. The program then asks theinstantiated object whether it does, in fact, exist in the PARODY data-base. Figure 6.3 is an example of that.

FIGURE 6.3 Retrieving a persistent object

Page 83: C++ Database Development 1558283579

Chapter 6: The Object Database and the Relational Database

We will revisit this and subsequent examples in Chapter 9, whichhas more detail about using the PARODY Persistent class memberfunctions.

Adding ObjectsA program adds an object to the PARODY database by declaring onethat does not exist, providing whatever data member values it needs,and telling the object to write itself to the database. Figure 6.4 is anexample.

Employee empl(123);if (!empl.ObjectExistso) {

--- the object does not exist/I ...

empl.AddObject();

FIGURE 6.4 Adding a persistent object

Changing an ObjectA program changes an object on the PARODY database by declaring onethat exists, changing the object's data members, and telling the object torewrite itself to the database. Figure 6.5 is an example.

FIGURE 6.5 Changing a persistent object

71

Employee empl(123);

if (empl.ObjectExists)) I-- the object exists

II... (change the data members)..empi .Change~bjecto;

Page 84: C++ Database Development 1558283579

72 c++ Database Development

Deleting ObjectsA program deletes an object from the PARODY database by declaringone that exists and telling the object to delete itself from the database.Figure 6.6 is an example.

Employee empl(123);if (empl.ObjectExistso) {

/--- the object exists

empi .DeleteObjecto;

FIGURE 6.6 Deleting a persistent object

Maintaining Object IntegrityThe database imposes limits on when a program is allowed to add,change, or delete an object. The program may not add an object thatalready exists. It may not add an object or change one if the new orchanged object is related to another object that does not exist in the data-base. It may not delete an object if other objects are related to it. Theapplication program needs to test for these conditions. PARODY reportsthe exceptions when the program tries to add, change, or delete objects inways that violate integrity. The errors might result from user input errors,for example, and the program would need to issue an error message tothe user. The AddObject, ChangeObject, and DeleteObject functionsreturn a true value if they permit the action and a false value if they donot. Figure 6.7 shows how these functions work.

if (!empl.AddObjecto)// --- add was rejected

if (!empl.ChangeObjecto)// --- change was rejected

if (!empl.DeleteObjecto)/- --- delete was rejected

FIGURE 6.7 Maintoining object integrity

Page 85: C++ Database Development 1558283579

Chapter 6: The Object Database and the Relational Database 73

Navigating a ClassA program locates an object by constructing it from the value of its pri-mary key. A program can also find an object in the persistent database byspecifying a value for one of its secondary keys. First, it instantiates anempty object, which the database does not search for. Then it initializesthe secondary key value and uses the FindObject function to find the firstobject of that class in the database that has the secondary key value.Figure 6.8, which assumes that the Employee class has SetDeptNo andDeptKey member functions, shows how that works.

Employee empl;empl.SetDeptNo(1); // initialize the dept#emp .FindObject(empl .DeptKey());if (empl.ObjectExistso) t

// --- at least one object exists

FIGURE 6.8 Finding on object with a secondary key

The program can retrieve the first and last persistent objects of a class inthe sequence of the key value by using the FirstObject and LastObjectfunctions, as shown in Figure 6.9.

FIGURE 6.9 Finding the first and last objects of a class

Employee empl;

empl.FirstObject(empl.DeptKey());

if (empl.ObjectExistso) f

// --- the object exists// ...

}

// ...

empl.LastObject(empl.DeptKey()):

if (empl.ObjectExists() f

// --- the object exists// ...

}

Page 86: C++ Database Development 1558283579

74 C++ Database Development

Following a FindObject, FirstObject, or LastObject function call, a pro-gram can navigate the persistent objects in a class in the sequence of thekeys by using the NextObject and PreviousObject functions, as shown inFigure 6.10.

FIGURE 6.10 Navigating a class

Navigating Among ClassesTo navigate class relationships, the program declares an object of a classthat has, as a secondary key, the primary key class of another persistentclass. It uses the secondary key value to declare an object of the otherclass. Then it processes that object and the following objects as shown inFigure 6.11, which assumes that the Department class has DeptNo andDeptKey member functions.

Page 87: C++ Database Development 1558283579

Chapter 6: The Object Database and the Relational Database

FIGURE 6.11 Navigating among classes

SummaryThis chapter compared the relational and object data models and offeredways to decide which you should use. Then it identified the features ofthe relational model that the PARODY system incorporates into theobject model. Chapter 7 digresses to discuss the design process for anapplication's object database.

ReferencesDatabase Modeling in the PC Environment, Bradley D. Kliewer, 1992,Bantam BooksAn Introduction to Database Systems Second Edition, C.J. Date, 1977,Addison-WesleyComputer Data-Base Organization Second Edition, James Martin, 1977,Prentice-Hall

75

Department dept(321);if (dept.ObjectExistso) I

Employee empi;empl. SetDeptNo(321);empl.FindObject(empl.DeptKeyo));

while (empl.ObjectExistso() &&empl.Dept~oo ~- dept.DeptNoo) f

empi .textObject(empl .DeptKeyo);II

Page 88: C++ Database Development 1558283579

Chapter 7

Designing an ObjectDatabase

Nothing is particularly hard if you divide it into smalljobs.

-Henry Ford

Object-oriented design is a big topic, one that a single chapter in a bookcannot do justice to. Designing a full object-oriented software system-particularly a large, complex one-is a major undertaking that shouldinclude the counsel and participation of experienced designers. One facetof an object-oriented design is its database. Most texts about object-ori-ented design concentrate on in-memory object hierarchies rather thandatabases, teaching you to visualize classes and build accurate and mean-ingful class relationships. If they get into database design at all, they usu-ally retreat to traditional relational methods, which is understandable;few object-oriented programming languages support a persistent objectdatabase. There are no systems to provide examples for a book.

This chapter explores the basics of object database design. You willlearn that any programmer can design a workable database by using

77

Page 89: C++ Database Development 1558283579

78 C++ Database Development

intuition and common sense and without applying or even understand-ing the mysteries of database administration. I contend that if you aresmart enough to write a computer program, you are smart enough todesign a database. I took the same position in C DatabaseDevelopment, asserting that database design is inherently easy, requir-ing only a few intuitive procedures to ensure a sound design.Thousands of programmers used the book and proved it. But that posi-tion could have been a potential target for harsh criticism by the prac-ticed experts of relational technology. It flies in the face of tradition andthreatens the sanctity of their priesthood. Taking the same positionwith an object database is safer. Few experts exist who can criticize it;the technology is too new.

Readers who used the earlier book will see few differences in thisapproach because the procedures for identifying and decomposing tradi-tional and object database requirements are similar.

This chapter avoids the arcane disciplines usually advanced as properdatabase design techniques. You won't need special forms, templates,rigid rules, or disciplined procedures for design review and refinement.This chapter does not promote a formal methodology. Those tools andprocedures have their place; they are for the big development projectwith a big system, a big staff, a big problem to solve, and structuredmanagement watching every move.

General Design GuidelinesDevelop a clear understanding of the problem you are trying to solve, thesolutions you might consider, and the ways to translate the solutions intoa database design. Be willing to modify the solution as it evolves.

Design an object database in small increments. Learn to isolate thefunctionally independent parts. Build individual components that havesingle points of interface. Deal with each component as if it were its owndatabase. Then integrate them as loosely as you can so that modificationsto one component do not interfere with the operations of the others. Ifyou build an Employee class and a Department class, build them sepa-rately. Except for the appearance of a department number in the employ-ee's class and the maintenance of that relationship, the two classes shouldbe independent. When an object of one class needs data from an object ofthe other or when one needs to update data in the other, add to the classinterfaces-the public member functions.

Page 90: C++ Database Development 1558283579

Chapter 7: Designing an Object Database 79

The 10 Steps of Object Database DesignThe design of an object database involves 10 steps taken, for the mostpart, in succession:

1. Identify the basis for the database requirements.2. Define the database's functional and performance requirements.

3. Identify the data items.

4. Separate the data members from the persistent classes.5. Build a data member dictionary.

6. Gather data members into persistent classes.

7. Identify the key data members of each class.8. Identify the relationships between classes.

9. Identify the class methods.

10. Add the inheritance and member functions to make the classespersistent.

There is always an 11th step, which is to reiterate the first 10. Let thesolution to the problem modify the problem, and let each successive solu-tion enhance your understanding of the problem. As it does, retrace yoursteps through the design process, stay loose, and change your results.Don't be afraid to throw out old work and start over. This 11th step,called refinement, is often left out.

Identify the BasisIf you are about to design a database, you must have a mission and apurpose. Someone has asked you to automate something. Who madethat request and how? How much did they know about the problemwhen they asked for a solution? Is the problem solved now? Is the pre-sent solution automated or not? Has anyone ever solved a similarproblem in a different environment? You need to define the functionaland performance requirements for the database, and the definition ofthese requirements should proceed from an understanding of the func-tions to be supported. You start by identifying the basis of that under-standing.

Page 91: C++ Database Development 1558283579

80 C++ Database Development

The basis for a database specifies the problem that is to be solved,what resources can be used in the solution, and some likely approachesto the solution. The problem specification can be as simple as a one-linestatement of objectives, or it can be a multiple-volume report. Consider apersonnel database. A problem specification for this database could be assimple as the one shown here.

Problem: Keep track of the employees within the departmentswhere they work, including their salaries and the dates theywere hired. Record the status of projects assigned to depart-ments. Record the employees who are working on projects.Distinguish managers from project workers. Distinguishdepartment managers from project managers.

The list of available resources includes such information as existingsource documents (employee time cards, for example), interfaces to otherdatabases, people who understand the function, and so on. Where appro-priate, include examples in the list. The resources list for the personneldatabase could resemble the following:

Company Personnel Action FormProject Work OrderEmployee Time Card

Next, you need to analyze the possible approaches to the problem. Be ascreative and unconventional as you want, but be careful. Make it clear toeveryone, including yourself, that these designs are preliminary. Don'tmake an emotional investment in a premature design, and don't getcaught in a situation where someone can hold you to one particularapproach.

To define the basis, start with the present solution. If there is no pre-sent solution, start from scratch.

Starting from an Existing SolutionIf the user is already using a computer to solve the problem, then youhave a good place to start. The existing system provides insight into therequirements for the database. The user has experience with an automat-ed solution and is able to tell you where the new solution can improve onthe old. An existing system is a good basis for a database design.

Page 92: C++ Database Development 1558283579

Chapter 7: Designing an Object Database 81

If the user works with a manual system, start there. Collect the func-tions that the system supports and turn them into functional and perfor-mance requirements. A manual system probably contains enough infor-mation to guide you in automating it. As such, it is a good basis for yourdesign. Usually the purpose of automation is to improve the system's per-formance, functionality, or both, in which case you can extend the basisto include the improvements.

Starting from ScratchIt is hard to imagine designing a database from scratch with no prior pro-cedure to use for a model. Almost everything has either been automatedor set up as a manual procedure. If a user asks you to initiate a complete-ly new procedure and solve a brand new problem, you should look for anexisting solution to a similar problem. Chances are, someone has alreadydeveloped one. If you do not find one, you must start from scratch. Thebest approach is to develop manual procedures, use them for a while, andallow them to influence the basis for a new database. If you cannot dothat, you must work with the user to develop an initial basis. Be advisedthat your design and implementation will no doubt change several timesas the database takes form and is used. You rarely see the best solutionthe first time.

Write Down the BasisOnce you have the basis for your database, write it down. Make it clearenough that anyone who understands the application can understand thebasis. Keep the written basis handy while you design and develop the sys-tem, and make updates to it as design changes occur. The basis is yourfoundation for the requirements analysis.

Define the RequirementsThis step enumerates the requirements for the database, addressing func-tional requirements and performance requirements separately. A databaserequirement should be clear and unambiguous. It should address itself toone specific aspect of the functions or performance of the system. Eachstatement should stand alone, and each statement should completely

Page 93: C++ Database Development 1558283579

82 C++ Database Development

define the requirement. Word it so that users and programmers alike canunderstand it. The requirements list is the planning document for the data-base. Anyone who has it and understands it should know what to expectfrom the system. Without it, you never know when you are finished.

Functional RequirementsFunctional requirements specify the kind of data that the database con-tains and the system's output. Document everything you know about thefunctions that are supported. Write about the problem but not the solu-tion. Say what, not how. Be specific in identifying pieces of informationthe database must know about. Here is an example of a list of require-ments for the personnel database.

1. The system will record and report the departments in the organi-zation by department number and name.

2. The system will record and report the organization's employeesby employee number. Each employee's record will include theemployee's name, salary, and date hired.

3. The system will record and report the organization's projects byproject number. Each project's record will include its name andschedule, which will be represented by the start and completiondates and the number of labor hours budgeted to and expendedagainst the project.

4. The system will record the assignment of employees to depart-ments. An employee is assigned to one department at a time, buta department may have many employees.

5. The system will record the assignment of projects to departments.A project is assigned to one department, and a department maybe responsible for many projects.

6. The system will record the assignment of employees to projects.An employee may work on several projects, including projectsassigned to departments other than the department to which theemployee is assigned. A project may have multiple employeesassigned to it.

7. The system will record the hours worked by each employee oneach project.

8. The system will record the assignment of an employee as depart-ment manager.

Page 94: C++ Database Development 1558283579

Chapter 7: Designing an Object Database 83

9. The system will record the assignment of an employee as managerof a specific project.

This list is a beginning. You would also develop requirements for specificqueries and reports, perhaps with accompanying screen or report layouts.Identify cyclic processes such as transaction posting cycles. Identify archiv-ing requirements. Such requirements can influence a database design.

Performance RequirementsPerformance requirements specify frequencies, speeds, quantities, andsizes-how often, how fast, how many, and how big-that the databasemust support. Some of the personnel system's performance requirementsmight look like this list:

1. The system will support as many as 10 departments.2. The system will support as many as 100 employees.3. The system will support as many as 20 projects.4. The system will support unlimited employee-to-project assign-

ments-all the employees can be assigned to all the projects.5. Retrieval of data recorded about an employee, project, or depart-

ment will be on-line and will be in response to the user's entry ofthe employee, project, or department number. The system willdeliver the related data within three seconds of entry of the asso-ciated control number.

This list, too, is only a beginning. The completed list would include per-formance criteria about backups, restart/recovery, scheduled maintenancedowntime, and database administration. In a multiuser environment, itwould specify access privileges and record and file locking requirements.This analysis must take growth into consideration. Make those numbersreflect the requirements for the future as well as for today.

Identify the Data ItemsHaving identified the requirements for the database, you can translatethose requirements into identifiable elements of data. First, you identifydata items. Later, those items will become classes and data members.

Page 95: C++ Database Development 1558283579

84 c++ Database Development

Delay that definition; it begins to define the solution, and, for now, youare seeking a clear and unambiguous expression of the problem.

Organize the data items that you identify. Use 3x5 cards, an on-linenotepad on your computer, or some other technique that allows you toitemize, sort, and shuffle.

Rummage around in the work you have already done, looking forpotential data items. From your basis and your requirements, extract refer-ences to things that look like data items. Start by pulling out the nouns.Each noun is a potential data item. Write the name of each onto a 3x5 card.Add to the card, everything else you know about the item highlighting theverbs. You'll use the verbs later when you identify the class methods.

The personnel system basis and requirements list delivers a collectionof nouns as shown in Figure 7.1.

managerhours expend

assignments

start date

project nameproject number

employee numberemployee name

namedepartment

departmenthours wor•e•-

hours hud.etedschedules

projectsdate hired

departmes -

employees

, 7

FIGURE 7.1 Basis and requirements nouns

You can use this list as an initial collection of the items to be recorded inand reported from the database. As your work proceeds, you can changethe list.

i

Page 96: C++ Database Development 1558283579

Chapter 7: Designing an Object Database 85

Separate the DataMembers from the ClassesYou now have a collection of data items. Some of them take the form of dataaggregates that represent persistent classes, or files, in the database. Othersare data members within those classes. This step distinguishes the two.

Unlike similar parts of traditional database design, this step does notclearly separate the aggregates from the data elements. In an object-ori-ented design, some of the so-called data elements are themselves abstractdata types, or classes. Your task is to recognize which of the data itemsare to be persistent classes and which are to be data members of persis-tent classes.

This is where you apply judgment, intuition, and guesswork. Look atthe data items you have collected. Which of these items seem to be indi-vidual data members, and which seem more like logically organizedaggregates of data members? Shuffle them up, sort them out, and movethem around. The 3x5 card method works well here. It should becomeobvious which items are members and which are not. A date, for exam-ple, is clearly a data member even though it is likely to be an instance ofan abstract data type, perhaps taken from a class library. It is not as obvi-ous that an assignment is not a data member. Can you see how a depart-ment number is a data member whereas a department is an aggregaterepresented by a number of data members, including the departmentnumber?The objective of this exercise is to separate the data members from thepersistent classes so that you can take the next two steps. The data mem-ber nouns from the list above are in this subset of the list:

+ salary+ date hired

+ hours budgeted+ hours worked

+ department number

+ department name+ employee name+ employee number

Page 97: C++ Database Development 1558283579

86 C++ Database Development

+ project number* project name

* start date

* completion date+ hours expended* manager

Build the Data Member DictionaryNow you begin to formulate the solution. Once you have identified whatthe data members are and have a list like the one above, build the datamember dictionary, which enumerates the various data members in thedatabase. It is important to itemize these members separately so that lateryou can identify the redundancies and interclass relationships.

The nouns in the list of data items are the ones that are not going tobe persistent classes; they are going to be members of persistent classes.Some of them are abstract data types. The salary is probably an instanceof a currency class. The dates are no doubt instances of a date class. Thenames are instances of a string class.

The data types for several data items are not apparent from this list.The employee number, department number, and project number could besomething other than simple integer types, depending on how the applica-tion defines them. The hours data items might be primitive integer types,or they might be abstract integer types with defined ranges. The format ofthe manager data item is unclear at this time. The requirement is to recorddepartment and project managers. You will address that one later.

You must collect everything that is known or that can be determinedabout each data member. At the very least, you need to know the datatype. If you are basing the design on an existing system, the documenta-tion-if any-can contribute to your dictionary. If the documentation isinadequate but the source code is available, you can reverse-engineer thedata member characteristics by seeing how the existing software usesthem. If you are automating a manual system, you can look at the entryforms (time cards, posting ledgers, and so on) to see how the manual sys-tem uses the data members. Sometimes these forms have accompanyingprocedures. You can use these procedures to find the descriptions of themanual entries and extrapolate the requirements for the data members.

Page 98: C++ Database Development 1558283579

Chapter 7: Designing an Object Database 87

Describing dates, Social Security numbers, telephone numbers, ZIPcodes, or shoe sizes is easy: These data members have known ranges, for-mats, and/or enumerated values, and there may be library classes thatyou can use. But other data members are not so well defined by commonusage. Quantities and amounts may have limits-how high can they get?Can they be negative? How many decimal places? And so on. Accountnumbers, vendor numbers, client numbers, employee numbers, part num-bers, stock numbers, customer numbers, and untold numbers of othernumbers are not universally defined. Many so-called numbers consist ofletters and punctuation as well as digits.

It is important that you clearly define the physical properties of all thedata members in the database. You must complete as much of this defini-tion as possible before you proceed with the design. When a data memberhas a clearly defined format and behavior, you should build a class toimplement the member as an abstract data type.

Gather the Data Members into ClassesWhen you separate data members from aggregates, the aggregates are leftover. You must deal with those aggregates, and chances are they are goingto be persistent classes. The following data items did not make it into thedata member dictionary:

+ employees

+ departments+ projects+ schedules

+ assignments

Look at your requirements now to see which of those items should bepersistent classes. None of the requirements calls for the permanent reten-tion of specific schedule records. The information that might be used toreport a schedule condition is information that would be recorded abouta project (the dates, the budget) or an assignment (the hours worked). Soyou can drop schedules from the list. Those that remain are potentially-but not absolutely-the persistent classes.

Now you can define the data representation of the persistent classes.To define a class, start with its data members. Use your trusty 3x5 cards.

Page 99: C++ Database Development 1558283579

88 c++ Database Development

Fan out the cards and pull out everything related to an employee. This isnot as simple as it sounds. It is obvious that the department name is notrelated to the employee and that the employee name is. But is the datamember that records the hours worked by an employee on a projectspecifically related to the employee? It is not, at least not immediately.You cannot put that data member into the employee's class because therecould be multiple values for that member for the same employee-theemployee can work on more than one project. The department name isnot related to the employee, but is the department number? Yes. Anemployee is assigned to one and only one department, and the depart-ments are identified by department number, so the department numbercan be a part of the employee record.

By examining each data member and viewing it in the light of itsrelevance to an employee, you can build a stack of 3x5 cards that arethe data members for the Employee class. This stack contains the cardsfor the employee number, employee name, date hired, salary, anddepartment number. Assuming that the abstract data types for dates,money, and strings already exist, and that the employee, project, anddepartment numbers can be integers, you can make a first cut at thedata representation of the Employee class design. Figure 7.2 shows howthat might look.

Note: Observe the use of typedefs to define types for controllingdata elements. By defining these type synonyms, you isolate theirdefinitions in one location. If you decide later that the employeenumber should be another type, perhaps even a class, you canmake the change in one place and recompile the program.

FIGURE 7.2 The Employee class

Page 100: C++ Database Development 1558283579

Chapter 7: Designing an Object Database 89

Next, you design the other persistent classes. Remember that you arenever finished. Always be willing to change what you have done before.Do not allow your current design to be constrained by the ones that pre-ceded it. If you cannot get something designed correctly because of anearlier design, then retreat and review that earlier design. Figure 7.3shows a first cut at the Department and Project classes, which assumethat data members such as hours-budgeted are unbounded integer types.

FIGURE 7.3 The Department and Project classes

The design has not yet considered the Assignment class, the requirementto record employee hours expended on a project, or the requirement torecord department and project managers. You have some design decisionsto make about these items. It's been easy until now because the design isproducing nice flat files. You'd almost think you were designing a rela-tional database.

There are several things to consider about the Assignment class. Therequirements state that an employee can work on several projects andthat a project can have several employees working on it. The system hasto record the hours expended by each employee on each project. Youhave three alternatives for this design. The relational database designerhas only one. The alternatives are:

* Build an Assignment class that records the employee-projectassignment and records the hours expended by the employeeagainst the project.

class Department {

DepartmentNumber deptno;

string name;

I;typedef int ProjectNumber;

class Project t

ProjectNumber projno;

string name;

int hours-budgeted;Date start date;

Date completion-date;};

Page 101: C++ Database Development 1558283579

90 C++ Database Development

* Put an array in the Employee class that contains an entry foreach project that the employee works on along with the hoursexpended.

+ Put an array in the Project class that contains an entry foreach employee working on the project and the hours expend-ed by that employee.

The relational database designer has the first alternative only. Whichalternative do you choose for the object database? Your decision shouldconsider the implications of each alternative. Consider the second alter-native. There can be 100 employees and only 20 projects. The projectarray could have, at most, 20 elements for any employee object. Thatseems more efficient than using the third alternative, in which a projectrecord might need an array of 100 employees.

Suppose you choose the second alternative and one of the require-ments is to retrieve all the employees that work on a project. Theretrieval would involve reading the entire employee file and scanning theproject array of each to pick out the employees assigned to the project.

Given that requirement, you choose the third alternative instead ofthe second. But suppose another requirement is to retrieve all the projectsthat a selected employee works on. You have the same problem inreverse.

That's not so bad, you say, because you do not have those require-ments. Well, what if the functional requirements change after the systemis running? Suppose those requirements are added to the basis. Moreover,it looks as if you do have one of those requirements. The requirement toreport hours expended against a project implies that the system needs todeliver the sum of the hours expended as recorded in whichever arrayyou chose. More about that later.

Not so bad, you say again, because the small number of employeesand projects does not represent a serious performance penalty when youscan those files. Suppose, then, that the performance requirements changeafter the system is running. Suppose the company grows to 1000 employ-ees and 200 projects. Suppose that the requirements expand to includemore data about each assignment, such as expense items, duty location,task identification, and deadlines. What happens to performance then?

Why did the founders of relational technology eliminate repeatinggroups such as these two arrays from the data model in the first place?The object model supports arrays, and there are valid uses for them: Anarray of points can define a graphic rendering; an array of coordinates can

Page 102: C++ Database Development 1558283579

Chapter 7: Designing an Object Database 91

define a map; an array of dots can define a screen; an array of notes candefine a concerto. The example of employee-project assignments is not,however, a good application for an array. By looking ahead, you can fore-stall problems by learning from the relational tradition and normalizingthe design. Remember that the design of a database is influenced more bywhat you must get out of it and how than by what you must store in it.

Figure 7.4 is the first cut at the Assignment class.

FIGURE 7.4 The Assignment class

The design is beginning to take form. But there is a problem. The systemmaintains the hours expended against a project as the sum of those hoursfor each employee. Suppose the employee leaves the project. What do youdo with the hours expended when you delete the Assignment object thatassociates the employee with the project?

Suppose the employee leaves the company. According to the rules ofinterclass integrity, you could not delete the employee object as long as anoth-er object was related to it-in this case, the object that associates employeesand projects. Once again, what do you do with those hours? Clearly, theProjects class needs to maintain an independent sum of hours expended, soyou must add that data member to its design. Furthermore, that sum cannotbe expected to reconcile with the totals for the project from the Assignmentobjects for the project because some employees may have left.

The requirement to record department and project managers is stillunresolved. A review of the requirements reveals that they are unclear asto whether a department or employee can have more than one manager.Furthermore, they do not specify whether the manager of a departmentmust be assigned as an employee of that department or whether the man-ager of a project must be assigned to work on that project. Here is a situ-ation in which you reiterate the design process, revisit the requirements,and make some modifications.

Assume that a department and a project can have only one managereach, that the department manager must be an employee of the depart-

Page 103: C++ Database Development 1558283579

92 C++ Database Development

ment, and that the project manager cannot charge labor hours to the pro-ject (and, therefore, may not be assigned as a worker on the project).How do you represent all that in the database?

There are some data interdependencies that a general-purpose data-base management system cannot enforce, and these appear to fall into thatcategory. By adding a department manager employee number data elementto the department class and a project manager employee number to theproject class, the database can reflect the required management positions.Figure 7.5 shows the two classes with their manager data items in place.

FIGURE 7.5 Managers added to departments and projects

The manager employee numbers in both classes satisfy the requirement torecord managers, but they introduce the potential for data disintegrity.

You now have an Employee object that points to a Departmentobject, which points to an Employee object. According to the require-ments, the department manager must be an employee of her own depart-ment. If the two objects do not point to each other, the database is out ofsynchronization.

Conversely, you have a Projects object that points to its manager'sEmployee object, which might point to an Assignment object-assumingthat the manager can work on projects other than his own-which pointsto a Projects object. According to the requirements, the project managermust not be a worker on his own project. If the chain of objects is com-pleted so that the project manager is assigned to his own project, thedatabase is out of sync again.

Page 104: C++ Database Development 1558283579

Chapter 7: Designing an Object Database 93

Maintenance of complex relationships such as these involves theapplication of a rule-based inference engine that is way beyond the scopeof most general-purpose database management systems. The relationaldata model does not solve the problem any better than the object modelcan. The responsibility for such database integrity remains with the appli-cation. Failure to take this responsibility does not create the same kind ofchaos caused by objects being related to nonexisting objects, a forbiddencondition that the data model can enforce. At worst, the database reflectsrelationships that the application prefers not to permit, and so theresponsibility is placed at the correct doorstep.

Identify the Key MembersA class that supports associative access must have a primary key datamember and can have several secondary key data members. The primarykey identifies objects of the class. The secondary keys identify alternativeways that you can find an object.

If the work you have done up to now includes a would-be persistentclass that has no uniquely identifying data member or combination ofdata members, then either the design is incomplete, the class cannot bepersistent, or navigational access is called for. If you cannot identify anobject, you cannot retrieve it.

This rule is consistent with relational theory, but some object-orienteddatabase systems do not support it. In those systems, an object acquires anidentity when it becomes persistent. The database manager assigns the iden-tity and returns it to the program that created the object. The applicationmust associate the system-defined identity with the object at a later time.

To be useful in a data-processing context, an object needs to beunique, so it follows that there is something about every useful objectthat sets it apart from others of its class. Otherwise, there is no point toits persistence. Whatever sets the object apart is its real identity, and anarbitrary one assigned by a database manager is just a handle.Associating true object identity with an internal handle should be in theprovince of the database manager and not something the applicationneeds to be concerned with.

If your persistent class lacks a primary key, perhaps you shouldredesign your persistent class. As you will learn later, there are valid per-sistent object designs that do not use key data elements. Most databasedesigns of any consequence-object-oriented or not-do, however, usesome method to index the objects.

Page 105: C++ Database Development 1558283579

94 C++ Database Development

The employee number is the primary key for the Employee class.Every Employee object has a unique employee number. The departmentnumber is a secondary key for the Employee class. Every Employeeobject has a department number, and multiple employees can have thesame department number.

The next step in the object database design is the identification of theprimary and secondary keys.

Primary KeysThe four persistent classes are defined. Now you can identify the datamember that, as the primary key, uniquely defines objects in each one.The Employee, Department, and Project classes are easy. The employ-ee number, department number, and project number serve that pur-pose. Figure 7.6 shows the class designs with primary key data ele-ments identified.The design in Figure 7.6 shows that keys are implemented with a tem-plate class. You add keys to the class by declaring parameterized Keytypes with instances in the class design.

What about the Assignment class? What is its primary key? It hasemployee number and project number data members. Can either of them bethe key? Because an employee can work on several projects, there are multi-ple Assignment objects with the same employee number. The employee num-ber cannot be the primary key. Because a project can have many employeesworking on it, there are multiple Assignment objects with the same projectnumber. The project number cannot be the primary key. What's left?

FIGURE 7.6 Primary keys identified

Page 106: C++ Database Development 1558283579

Chapter 7: Designing an Object Database 95

FIGURE 7.6 Continued

Remember from Chapter 2 that a many-to-many relationship such as theone that exists between employees and projects is represented in a rela-tional database by a connector file that has as its primary key the con-catenated primary keys of the related files. The Assignment class is theobject database equivalent of a relational connector file. Therefore, itsprimary key is the concatenation of the employee number and the depart-ment number. That combination of data members uniquely identifieseach Assignment object. Figure 7.7 shows the addition of the concatenat-ed primary key to the Assignment class.

class Assignment fICatKey(EmployeeNumber ,ProjectNumber> assignment;i nt hours-expended;

FIGURE 7.7 Concatenated primary keys

Concatenated keys are also implemented with a template. In this case, thetwo data members-emplno and proino-are replaced with one instanceof a CatKey parameterized type consisting of the employee and projectnumbers and given the name assignment.

Key<DepartmentNumber> deptno; //primary department keystring name;Empi oyeeflumber manager;

class Project tKey<ProjectNumber> projno; IIprimary project keystring name;int hours-budgeted;Date start-aete;Date completion-date;EmployeeNumber manager;

key

Page 107: C++ Database Development 1558283579

96 C++ Database Development

Secondary KeysSecondary keys are the ones that provide alternative access to the objectsof a persistent class. In theory, you can retrieve any subset of objects byserially retrieving all the objects in a class and testing them against yoursearch criteria. Many times, this is appropriate. A monthly or yearlyreport does not need its own index to support its unique sort require-ments. Other requirements are for on-line access, and they can use sec-ondary keys. If you frequently retrieve all the employee objects ofemployees who work for a selected department, then the departmentnumber in the Employee class should be a secondary key. If the recep-tionist looks up the telephone extensions of employees based on theirnames, then the employee name should be a secondary key in theEmployee object. Figure 7.8 shows the Employee class with the depart-ment number changed to a secondary key.

FIGURE 7.8 Secondary keys

Secondary keys support class relationships, which are discussed in the nextsection. When a secondary key is the same data item as the primary key ofanother class, an implied relationship exists between the classes. To main-tain those implied relationships, you would probably turn the managerdata members in the Department and Project classes into secondary keys.

In a connector class such as the Assignment class, the primary key is theconcatenation of two key members, which are primary keys of the two class-es being associated in a many-to-many relationship. Each of the two keymembers in the Assignment class must be half of the primary key, and thetwo keys must also be secondary keys. This permits the system to locate thefirst instance of an object with a specified value for either half and to navigatethe connector objects, retrieving the connections for the specified connectedobject. The CatKey template provides that the concatenation of the two typesis a primary key and that the two types are themselves secondary keys.

Page 108: C++ Database Development 1558283579

Chapter 7: Designing an Object Database 97

Identify the (lass RelationshipsIn an object database, persistent classes are related when they contain acommon data member and when that data member is the primary key inone of the classes. The Employee class in Figure 7.8 is related to theDepartment class because the employee record includes the departmentnumber data member as a secondary key, and the department number isthe primary key to the Department class.

Because of this relationship, you can view the Department class fromtwo perspectives based upon the two purposes it serves. First, it is the sys-tem's record of everything related to a department. All by itself, it couldbe a Department database rather than a class in a larger database. Exceptto identify its manager, it needs nothing from the Employee class to fulfillits purpose. Second, the Department class is the table of informationabout the department in which an employee works. It validates thedepartment number being stored in an employee's record and providesthe department name when you retrieve the employee.

For the implied relationship to work, it must have integrity. If anEmployee object contains a department number, there should be a match-ing Department object.

In your design, you evaluate each potential relationship to seewhether it is real. Sometimes a control number is included in a class asinformation only. Users do not care whether the corresponding object stillexists; it may have been retired. For this reason, database managementsystems do not usually automatically infer a relationship simply on thebasis of shared key data members. The database designer must explicitlydeclare the relationship so that the database manager knows whether toenforce it or ignore it.

More often, the relationships are real and must be protected. In one-to-one and one-to-many relationships, the software must preserve dataintegrity. In the case of the many-to-many relationship, the connectorclass supports the relationship, and it must always be synchronized withthe two classes it connects.

The relationships between classes can describe potential retrievalpaths for multiple-class retrievals. Sometimes, the apparent paths areincorrect. Observe the relationships between the Employee,Department, and Project classes. If you designed a retrieval thatbegan by retrieving a department object, each project for the depart-ment, and then each employee who is assigned to each project, youmight not have the answer you are looking for. If the purpose of the

Page 109: C++ Database Development 1558283579

98 C++ Database Development

retrieval is to list the employees who are working on projectsassigned to the department, the response is correct. But if theretrieval is supposed to deliver a list of employees who work in thedepartment, then the response is incorrect. In this case, you havechosen a retrieval path based on interclass dependencies that don'twork. Remember from the requirements that an employee in onedepartment can work on a project that is assigned to a differentdepartment. The database design supports these relationships, but, asseen in this example, the potential exists for you to describe pathsthat deliver incorrect results. This is not a flaw in the databasedesign. The design is correct. The problem lies in the description of aretrieval path. You must develop these paths carefully to ensure thatthe answer is the one you are looking for.

Identify the MethodsAn object database consists of objects of persistent classes. A C++ classcontains its data representation and its behavior. There is often a mis-understanding about what this means. Some object-oriented program-mers believe that an object-oriented database stores the methods alongwith the data. This is not practical for a C++ program. The methodsare the public member functions-compiled by the compiler, catalogedin relocatable libraries, linked by the linker to your application pro-grams, and stored with them in executable binaries. Those executablefiles are where the methods are kept. The compiler associates them withthe persistent objects by virtue of the class definitions. There is nomagic involved.

Nonetheless, you must identify the methods for your persistent class-es. This part of the design is exactly the same as for the design of anyother C++ program. Recall that when you built those 3x5 cards, youmade notes on them about the data items. Return to the ones that referto data items that became persistent classes. Look at the verbs in yournotes. Those verbs are the first step toward defining the application-dependent behavior of the persistent classes.

When you identify how the application processes the data members,you reveal requirements for member functions that let the applicationread and change the data members. Does the system need a value that itcomputes from among the data members of a class? Do you need thehourly rate for an employee to compute charges against a project? Add a

Page 110: C++ Database Development 1558283579

Chapter 7: Designing an Object Database 99

member function to the Employee class to do the computation. Figure7.9, which assumes a work year of 2080 hours, shows how that wouldwork.

const int hours-year =2080;class Employee{

Money salary;public:

virtual Monley Rate()f return salary / hours-year; J

FIGURE 7.9 A persistent class method

The example in Figure 7.9 is a small one, but it makes this point: Addobject-oriented methods to your persistent classes the same way you do inany other C++ program design. The so-called persistent method is boundto the persistent object, not because some unknown operating-system/database-system magic wand stores the method in the persistentobject database, but because the program that retrieves the object uses thesame class definition and class library used by the program that createdthe object.

Make the (lass PersistentThe last step-except for continuing to iterate-is to integrate thepersistent object class design into the persistent object database man-agement system. You need to add attributes to the key data membersand to the persistent classes so that they can participate in their ownpersistence.

The nature of those attributes depends on the database managementsystem that you use. With the PARODY software contained in this book,those attributes consist of implementing inheritance, calling some baseclass functions at the correct times, and adding some special memberfunctions to the persistent class that the base class calls. Figure 7.10shows the beginnings of that design.

Page 111: C++ Database Development 1558283579

100 C++ Database Development

FIGURE 7.10 The start of a persistent object database

Returning to the design notation from Chapter 3 (Figure 3.5), we canrepresent the persistent object database as shown in Figure 7.11.

We will complete this design in Chapter 10 when we discuss this andseveral other example object-oriented database applications.

typedef int EmployeeNumber;

typedef int DepartmentNumber;

typedef int ProjectNumber;

class Employee : public Persistent

Key<EmployeeNumber> emplno;

string name;

Date date-hired;

Money salary;

Key<DepartmentNumber> deptno;

class Department : public Persistent

Key<DepartmentNumber> deptno;

string name;

Key<EmployeeNumber> manager;

class Project : public Persistent I

Key<ProjectNumber> projno;

string name;

int hours-budgeted;

Date start-date;

Date completion-date;

Key<EmployeeNumber> manager;};

class Assignment : public Persistent t

CatKey<EmployeeNumber,ProjectNumber> assignment;

int hours-expended;};

Page 112: C++ Database Development 1558283579

Chapter 7: Designing an Object Database 101

DEPARTMENT:

FIGURE 7.11 A personnel database

SummaryNow you know what constitutes an object database and how to designone. A database design needs a software system to process it. You'vedone some of that already. The member functions of the classes youdesigned are a large part of your database application. Database softwareconsists of two parts: those applications programs that process theobjects-from within and outside the object-and the general-purposesoftware that manages database organization, storage, and retrieval. Thissoftware is called the database management system. Chapter 8 introducesPARODY, the database management system.

ReferencesC Database Development, Second Edition, Al Stevens, 1991, MIS:Press

Page 113: C++ Database Development 1558283579

Chapter 8PARODY

Parodies and caricatures are the most penetrating ofcriticisms.

-Aldous Huxley

PARODY is the Persistent, Almost Relational Object Database manage-ment system implemented in this book as a C++ class library. PARODYachieves the objectives identified in Chapter 5. It implements a persistentobject database with a relatively simple interface to its classes. Its datamodel resembles the relational model in some respects and assumes theproperties of C++ objects in others-a parody of sorts. This chapter andthe next introduce PARODY and show you how it works. This chapterdescribes how to define the classes in a PARODY database and howPARODY and the classes coordinate the persistence of objects of theirown type. Chapter 9 discusses how an application program declares anduses the persistent objects.

PARODY Class HierarchyFigure 8.1 shows the class hierarchy that PARODY uses to implementpersistent objects. You are primarily concerned with the four classes at

103

Page 114: C++ Database Development 1558283579

104 c++ Database Development

the lowest level of this hierarchy. The Key<T> and CatKey<T1,T2> tem-plate classes encapsulate the operations of index key data elements. ThePersistentObject<T> template is one that you can use to manage the per-sistence of flatly structured class definitions without keys. Persistentobjects with complex class structures and primary and secondary keysare derived from the Persistent class.

FIGURE 8.1 The PARODY class hierarchy

The classes above the bottom level in Figure 8.1 are mostly internal toPARODY. The Parody class encapsulates the database. You instantiate anobject of this class to open and process the database. It contains DataFileand IndexFile objects to manage the persistent storage of data objectsand the indexes into them. It maintains a list of currently instantiatedpersistent objects. The DataFile and IndexFile classes are derived fromthe NodeFile class, which manages disk input and output of Nodeobjects-disk records. The TNode class is a specialization of the Nodeclass to add the behavior of the index B-Tree mechanism. The PdyKeyclass is the base class for the Key<T> and CatKey<T1,T2> template class-es. The Persistent class is the base class for all persistent objects. It main-tains a list of the object's keys and other data members associated withthe object's storage and retrieval.

Page 115: C++ Database Development 1558283579

Chapter 8: PARODY 105

Defining a PARODY DatabaseDefining a set of related persistent classes is equivalent to building a data-base schema that describes the contents of the files. Most traditionaldatabase management systems use a data definition language to describethe database schema. PARODY uses the C++ class definition into whichyou encapsulate not only a description of the data members in each classbut also the integration of the class with the PARODY database manager.

You define a database by designing classes that represent the persis-tent objects and by designating the key data members of those classes.The persistent object and key member classes derive from abstract baseclasses defined in the PARODY class library. Objects of the persistentclass type cooperate in their own persistence by calling PARODY mem-ber functions at specific times and by providing member functions thatPARODY can call to perform tasks that require knowledge of the contentand format of the persistent object.

Declaring the PARODY DatabaseTo use persistent objects, a program must first declare the persistentobject database first. PARODY includes a class named Parody. You mustdeclare an object of this class for each database. The object must be inscope for as long as your program creates and retrieves persistent objects.The address of the object must be available to the constructors of yourpersistent classes. Figure 8.2 is an example of how a program declares aParody database object.

Parody *personnel;void main()

personnel = new Parody("PERSONEL");Employee empl (123);Department dept(5);// ... .

delete personnel;

FIGURE 8.2 Declaring a PARODY database

Page 116: C++ Database Development 1558283579

106 C++ Database Development

The "PERSONNEL" initializer in the Parody declaration is the name ofthe database. PARODY uses it to name two files-one for the objects andone for the index. These files are discussed later in this chapter. The ini-tializer is in the format of a file name without a file extension. In MS-DOS systems, for example, the initializer must be a valid one-to-eight-character DOS file name. PARODY adds the file extensions ".DAT" and".NDX" to the two file names.

If you declare a Parody database and none exists with the name ofthe initializer, PARODY builds a new database with that name, and youcan begin using it by creating persistent objects for it.

Defining the Persistent Object ClassA PARODY object is an instance of a class. You define the class, giving itattributes that make it persistent in the Parody object. PARODY providesthe base classes and the interface that make it possible. Your class man-ages its own persistence with the help of the behavior it inherits fromPARODY.

CLASS DEFINITION

A persistent class derives from the Persistent abstract base class, which isdefined by PARODY. The persistent class usually includes a primary keydata member. It must contain a constructor, a destructor, and two privatemember functions named Read and Write. Figure 8.3 is a Departmentclass, which represents the minimum persistent class along with a namedata member and member functions in the class interface to set and readthe department name. This class and the other example classes in thischapter figure prominently in the case studies of Chapter 10.

The Department class in Figure 8.3 acquires the persistent attribute byderiving from the Persistent class. Except for the base class specification,the only visible things that distinguish the Department class from a non-persistent class are the Read and Write member functions. The constructorand destructor also have a role to play in the class's persistence. The fol-lowing sections address the four basic cooperating member functions.

PERSISTENT OBJECT CONSTRUCTOR

The constructor has three responsibilities in a persistent class. First, it ini-tializes the base Persistent constructor with a reference to the Parody

Page 117: C++ Database Development 1558283579

Chapter 8: PARODY 107

database object. Second, it initializes the persistent object's primary keydata member to identify the object. Then, after any custom constructionof its own, the constructor calls the LoadObject member function in thePersistent base class. Figure 8.4 is an example of a persistent class con-structor function.

FIGURE 8.3 A persistent object

FIGURE 8.4 The persistent object constructor

Usually, the constructor's custom construction consists of setting the datamembers to null values in case the specified object is not in the database.If other data members need special construction, such as allocating mem-

typedef DepartmentNumber int:

class Department : public Persistent {Key<Departfte#tNuimber> deptno;string name;void Reado;void Writeo;

public:Department(Rnt dn - 0);-Departmento;string &Name(

f return name:; Ivoid Sett~ame(string& not)

{name - not;

Page 118: C++ Database Development 1558283579

108 (++ Database Development

ory from the free store, the constructor does it before calling LoadObject.If construction of a data member depends on its representation in thedatabase, such as the number of elements in an array, the persistentobject's Read member function can do it.

The constructor should call the LoadObject function even if the keydata member value is null. PARODY uses the first call to LoadObjectfrom a class in a program's execution to capture the class's key datamember configuration.

You can omit the second step-that of identifying the database-ifthe persistent object is stored in the most recently declared Parody objectdatabase. PARODY automatically associates the object with the mostrecently declared Parody object that is still active-in scope and notdestroyed. When you use that option, the database reference does nothave to be global. The personnel pointer in Figure 8.2 can be declaredwithin the main function, for example. Figure 8.5 illustrates this usage.

Department::Department(Departmenttumber dn) deptno(dn)

IIvoid main()

SI Parody *personnel - new Parody("PERSONEL");

delete personnel;

FIGURE 8.5 Using the default open database

PERSISTENT OBJECT DESTRUCTOR

The persistent class destructor has one responsibility. It calls theSaveObject member function in the Persistent base class before complet-ing its own destruction. Figure 8.6 is an example of a persistent classdestructor function.

Often, the SaveObject function call is all the destruction that thedestructor needs. If any data members need further destruction, such asdeleting allocated free store memory, the destructor does it afterSaveObject returns.

Page 119: C++ Database Development 1558283579

Chapter 8: PARODY 109

Department: :-Department()

SaveObjecto;

FIGURE 8.6 The persistent object destructor

Read MEMBER FUNCTION

The persistent object's class constructor calls the Persistent base class'sLoadObject function to locate and read an object from the PARODYdatabase. That function in turn calls the derived class's Read function.Read is defined in the Persistent base class as a pure virtual function, soall derived classes must include one.

The Read function reads the data members into the persistent objectby calling the base class's ReadObject function for each data member.Figure 8.7 is an example of a persistent class's Read function.

FIGURE 8.7 The Read member function

The Read function calls the base class's ReadObject function once foreach data member in the object. The ReadObject function accepts a refer-ence to the data member and does the physical input operation into thedata member. The ReadObject function has overloaded versions for allthe intrinsic C++ types, and it understands certain generic abstract datatypes, such as string and Date, but it does not understand specific onessuch as the Key<DepartmentNumber> deptno object. For that reason, theprogram reads the data into a local variable and then assigns the localvariable to the deptno object's data member. SetKeyValue is a member

void Department::Read(){

DepartmentNumber dpno;ReadObject(dpno);deptno.SetKeyValue(dpno);ReadObject(name);

}

Page 120: C++ Database Development 1558283579

1 10 C++ Database Development

function of the Key template class. The function takes an object of theparameterized type and assigns it to the key data member.

It might seem odd that the persistent derived and base classes bounceback and forth in this series of function calls. The derived class's con-structor calls the base LoadObject function, which calls the derived Readfunction, which calls the base ReadObject function. Later in this chapteryou'll learn why. The same pattern applies to the Write function, dis-cussed next.

If the object contains variable-length fields such as arrays, lists, ordata items in dynamic free store allocations, the Read function constructsthem. To know how many array or list elements to construct or howmuch free store to allocate-and, therefore, how many data items toread-the Read function first calls ReadObject to get an integer valuethat represents the dimension of the array or the buffer size. The Writefunction will have written that value when the object became persistent,as you'll see next.

Write MEMBER FUNCTION

The persistent object's class destructor calls the Persistent base class'sSaveObject function to save new or changed objects in the PARODYdatabase. That function in turn calls the derived class's Write function.Write is defined in the Persistent base class as a pure virtual function, soall derived classes must include one.

The Write function writes the data members from the persistentobject by calling the base class's WriteObject function for each datamember. Figure 8.8 is an example of a persistent class's Write function,which is the reciprocal of the Read function.

FIGURE 8.8 The Write member function

The Write function calls the base class's WriteObject function once foreach data member in the object. WriteObject understands the format of

Page 121: C++ Database Development 1558283579

Chapter 8: PARODY 111

certain generic abstract data types the same way that ReadObject does.KeyValue is a member function of the Key template class. It returns theparameterized type object that represents the key's value.

If the object contains variable-length fields such as arrays, lists, ordata items in dynamic free store allocations, the Write functionshould not destroy them after it writes them. The destructor shoulddo that. To tell the PARODY copy of the object how many array orlist elements or how much free store it contains for a particular datamember, the Write function first calls WriteObject to write an integervalue that represents the dimension. The Read function uses thatvalue to read the object into memory when the object is instantiatedat some later time.

The reason that you do not destroy the memory in Write-eventhough you might have allocated it in Read-is that programs can callSaveObject from elsewhere than in the destructor simply to refresh thedatabase with current changes to the object.

Defining KeysFigure 8.3 shows a persistent class definition in which the first data mem-ber is a Key<DepartmentNumber> object. The Key template gives thedata item the behavior of a key and registers the key with the Persistentclass currently being constructed.

A key is one of the data members of a persistent class. Its data valuecan be an object of any fixed-length type, but it must be a parameterizedtype within the Key template class.

A key's fixed-length property is critical to its operation. This require-ment is levied by the B-tree algorithm, PARODY's indexing mechanism,which works with fixed-length key values. Appendix B explains the B-tree algorithm at some length.

The Key template class determines the length of a key from thesize of the object that it parameterizes. If the object's type is a C++intrinsic type or a user-defined type represented by a flat structure (nopointers, references, virtual functions, arrays, or variable-lengthfields), then all that is needed to establish the key is its declarationwithin the persistent object in the same way that Figure 8.3 declaresthe deptno variable. If the key is not a flat structure or a string object,you must provide specialized Key<T> member functions as describedin the next section.

Page 122: C++ Database Development 1558283579

112 C++ Database Development

Specialized KeysMost applications can use the Key<T> template class as is. Occasionally,however, you need to support an indexing type more complex than theintrinsic C++ types. In that case, you must provide specialized memberfunctions for the Key<T> template. Figure 8.9 is an example of a special-ized key that uses a user-defined type named MyClass, which is assumedto have two data members of unspecified types named datamemberl anddatamember2. The example provides a few details and serves as a modelfor you to use when you build your own specialized keys.

-- - - - --- - - - - - - - -

IIspecialized Key<MyClass> template member functions

II--- construct a key from an object of the typeKey<MyClass>::Key(const MyClass& key) :ky~key)

keylength - sizeof datamemberi + sizeof datamember2;IIetc ....

void Key<MyClass>: :ReadKey(IndexFile& ndx)

1/- -- one of these for each data memberndx.ReadData(&datamemberl, sizeof datamemberi);ndx.ReadData(&datamember2, sizeof datamember2);

void Key(MyClass>: :WriteKey(IndexFile& ndx)

II--- one of these for each data memberndx.WriteData(&datamemberl, sizeof datamemberi);ndx.WrlteData(&datamember2, sizeof datamember2);

PdyKey *Key<MyC1 ass>: :MakeKey() constI

Key<MyClass> *newkey = new Key<tMyClass>: // null keynewkey->SetKeyLength(keylength);newkey->ky.datamemberl -0;

FIGURE 8.9 A specialized key

Page 123: C++ Database Development 1558283579

Chapter 8: PARODY 113

newkey->ky.datamember2 - 0;return static_cast<PdyKey*>(newkey);

}

bool Key<MyClass>::isNullValue() const

// --- return true if the key is nullreturn datamemberl - 0 && datamember2 - 0;

Iint Key<MyClass>::operator>(const PdyKey& key) const{

Key<MyClass>& ky - dynamlc-cast<Key<MyClass>&>(key);if (datamemberl - ky.datamember2)

return datamember2 > ky.datamember2;return datamemberl > ky.datamemberl;

}

int Key<MyClass>::operator--(const PdyKey& key) const{

Key<MyClass>& ky - dynamic-cast<Key<MyClass>&>(key);return datamemberl - ky.datamemberl &&

datamember2 - ky.datamember2;I

PdyKey& Key<MyClass>::operator-(const PdyKey& key){

Key<MyClass>& ky - dynamic-cast<Key<MyClass>&>(key);datamemberl - ky.datamemberl;datamember2 - ky.datamember2;

I

FIGURE 8.9 Continued

OVERLOADED RELATIONAL OPERATORS

The overloaded greater than (>) and equal to (==) operator functionslogically compare the values in two key objects. These functions per-mit the PARODY indexing algorithm to order and locate keys in the B-tree. If your application does similar comparisons, you need to includeadditional overloaded relational operator functions in the public classinterface.

Page 124: C++ Database Development 1558283579

1 14 C++ Database Development

OVERLOADED ASSIGNMENT OPERATOR

The overloaded assignment operator assigns the contents of a Key<T>object to another one of the same class. If your application also assignskey objects, you might include an overloaded assignment operator in thekey class's public interface that uses the derived class type. Do not useyour overloaded operator to assign new key objects to the key objects inthe persistent class. The ones in the class have internal control values thatmust not be disturbed. It's better to simply work with the key's data val-ues and ignore its Key properties outside the class.

ReadKey AND WriteKey MEMBER FUNCTIONSThe ReadKey and WriteKey member functions read and write the key'sdata values from and to a specified IndexFile object. These functionsmust always read and write the same number of characters for a particu-lar key.

Do not assume that because you have these functions you do notneed to read and write the key data members from within the persistentclass. These functions are to support the B-tree index files only. Becausethe application does not normally have visibility into the index files, youshould include the index data values in the ReadObject and WriteObjectcalls in the derived persistent class's Read and Write functions.

THE MakeKey FUNCTIONEvery key must provide a MakeKey function. It constructs a null objectof the derived key type with the C++ new operator and returns itsaddress. The Key<T> class does not delete this construction. PARODYcalls the MakeKey function for two purposes. First, it builds emptybuffers of the correct size into which it reads keys. Second, it identifiesthe key class with respect to the class identification of the objects itindexes and the relative position of the key object within the persistentclass. The callers of the MakeKey function handle the delete. Your pro-gram should never need to call the MakeKey function; nonetheless, if youare specializing a Key<T> you must provide it.

THE isNullValue FUNCTIONThe isNullValue function tells PARODY whether a key value is null. Themeaning of null depends on the data members that constitute the key itself,so the persistent class's keys must provide the information. PARODY does

Page 125: C++ Database Development 1558283579

Chapter 8: PARODY 115

not attempt to verify class relationships when the relating key value is null.This allows you to add objects to the database when two classes are mutu-ally related.

For example, the Employee class is related to the Department class;an employee is assigned to a department. The Department class is relatedto the Employee class; each department object has an employee assignedas the manager. You must be able to add objects with null relating keys,because the database is empty to start, and you must be able to add eitheremployees without department numbers or departments without manageremployee numbers as a first step.

String KeysWhen a key is an object of the standard string class, PARODY suppliesthe specialized Key<string> member functions. The only thing that youmust do is specify the key's fixed length in the constructor. Figure 8.10shows two ways to do that.

FIGURE 8.10 Setting the length of a Key<string> object

const int keylen -25;// --- set key length in default constructor parameterclass Person : public Persistent {

Key<string> name;void Reado);void Write();

public:Person(const string& nm - string('\O', keylen));

I ;// --- set key length with Key<string>::SetKeyLengtho)class Place : public Persistent {

Key<string> name;

public:Place(const string& nm - string()) : name(nm)

{ name.SetKeyLength(keylen); LoadObjecto; I

1:

Page 126: C++ Database Development 1558283579

116 C++ Database Development

The first technique, used by the Person class in Figure 8.10, uses adefault constructor parameter with the key length specified in the con-struction of the Key<string> argument. In the second technique, the con-structor calls the Key<string>::SetKeyLengthO function before callingLoadObject.

Once a key is established with its length in the database, youshould use the same length when performing operations that use thekey. If you try to change an established key length, PARODY throwsan exception.

The Read and Write functions for the Persistent derived class read andwrite the string values from and to the database, as shown in Figure 8.11.

FIGURE 8.11 Read and Write key values

Secondary KeysThe first Key<T> object that the persistent class declares is its primarykey. All other Key<T> objects are secondary keys. The position of the keydata members, therefore, determines which one is the primary key. Allexcept the first one are secondary.

PARODY treats primary and secondary keys differently. A class inthe database may have only one object with a particular primary keyvalue. There may be only one project with project number 123, forexample, but several projects may have the same manager.

void Person::Read()

string fn;:ReadObject(nm);name .Set Key Va] ue(fnm);

void Person: :Write()

Wri teObject(name. KeyVal ueCD;

/ 1 ! i i id i i i i• !~ ; !i i~ e i~ i ii ! ~ ~ ii~iiii i iiii i ii iii~!i~ ii •iiii•!j ii~ iiii! l !;ii i!ii•l!ii i•ii!i•iii!ii!ii•ii l•ii ii!i~ iii~l! f !i iii :i I~!i iii i•!iii i ii i~ i~ i ii ! iiiii !i!!i~ ~ ~ ii ii ; igii!i~ i ! i i ! • i i i• i! !i ! i i ii ii i • ! i i i~ ~~i i !i~ i~ ii~ ! ii ii~ii i i i i~ ~ i i ii !i i ii! i !i i ii !l i !i ! • i •li i i • i¸¸ i ~ i i !i !i! i i iiii !i ! i i

Page 127: C++ Database Development 1558283579

Chapter 8: PARODY 117

Concatenated KeysWhen a database design calls for the relational equivalent of a connectorfile, the derived Persistent class includes a concatenated key as its primarykey. A concatenated key contains two other key objects as its data values.Each of them is a primary key to another class and is treated as a sec-ondary key to the connector class. The application program can retrieveobjects based on either key value or the combination of the two.

For example, to represent the many-to-many relationship in whichemployees can work on many projects and each project can have manyemployees working on it, Figure 8.12 expands on the Assignment classfrom Chapter 7.

FIGURE 8.12 A class with a concatenated key

The program instantiates an Assignment object by specifying theEmployeeNumber and DepartmentNumber values. The constructor ini-tialization list initializes the assignment concatenated key object withthose values.

Relating ClassesObjects in one class can be implicitly related to objects in another classby reference. If a class has a secondary key of the same Key<T> type as

class Assignment [CatKey<EmployeeNumber, ProjectNumber> assignment;int hours-expended;

public:

Assignment(EmployeeNumber en = 0, DepartmentNumber pn = 0):assignment(en, pn)

h{ ours-expended - 0; LoadObjecto; I-Assignment()

f SaveObjecto; I

Page 128: C++ Database Development 1558283579

1 18 c++ Database Development

the primary key of another class, an implicit relationship exists. You mayor may not elect to let PARODY enforce that relationship. For example, itmight be acceptable to retain assignments information long after anemployee has left or the project has closed down. On the other hand, itmight not be acceptable to delete an employee who is still on record as themanager of a department. The degree to which the system enforces therelationships is a function of the application itself. Therefore, PARODYrequires that you identify the relationships that you want it to enforce.Figure 8.13 shows how a class specifies a relationship by calling thePersistent base class's Relate member function from within the constructor.

FIGURE 8.13 Relating classes

The Relate function call in Figure 8.13 tells PARODY that the Projectclass and the Employee class are related. PARODY does not allow you toadd or change a project object in which the manager data item is non-null and represents an Employee object that is not in the database. TheRelate function call is all that you need to enforce that relationship.

Conversely, PARODY does not allow you to delete an Employeeobject if any related Project objects still exist. However, you might needto take one more step to ensure this enforcement. PARODY builds atable in memory of classes and their relationships as you declare objects.It does not know about the relationships between classes until a construc-tor calls the Relate function. Therefore, if a program deletes an Employeeobject but that program has never declared a Project object during thecurrent execution of the program, PARODY does not know about the

Page 129: C++ Database Development 1558283579

Chapter 8: PARODY 119

relationship because no Project object has called the Relate function torecord it. Lacking knowledge of the relationship, PARODY deletes theEmployee object regardless of whether any Project objects exist in thedatabase that refer to the employee being deleted.

To ensure that PARODY enforces all relationships, instantiate anempty object of each class that calls Relate from its constructor. Do thisat the beginning of the program. You can let the empty objects go out ofscope right away. Simply having them declared once is enough to recordthe relationships for the duration of the program's execution. Figure 8.14shows how a program handles that.

FIGURE 8.14 Ensuring relationships

Observe that the program in Figure 8.14 declares the four empty objectsfrom within a brace-surrounded block. This form allows them to goimmediately out of scope so that they do not occupy stack space for thebalance of the program's execution.

Multiple-Copy ObjectsNone of the persistent class examples in this chapter has copy construc-tors or overloaded assignment operators. This is not an oversight. As arule, you do not want to make in-memory copies of objects. Makingcopies of a persistent object has its perils. PARODY would not knowwhich copy to save on the database when the object goes out of scope.

int main(){

Parody *personnel - new Parody("PERSONEL");{

-- -.-. empty objects to declare relationshipsEmployee empl ;Department dept;Project proj;Assignment assgn;

// ...

delete personnel;return 0;

Page 130: C++ Database Development 1558283579

120 C++ Database Development

Therefore, PARODY must ensure that there is only one copy of any par-ticular persistent object in memory at a time.

There are several ways that a C++ program copies objects. The copy con-structor and assignment are two. In a third way, the program declares anoth-er instance of the same object. Rather than allow these processes to makecopies of an object, the Persistent class must take measures to prevent it.

When a persistent object is constructed, PARODY adds a reference to alist of currently instantiated persistent objects. If the program attempts toinstantiate a copy of an instantiated object, PARODY throws an exception. Ifthe application program does not catch the exception, the program aborts.

The exception thrown is a pointer to type Persistent, and the value of thethrown pointer is the address of the first instantiation of the object. When PAR-ODY throws the pointer, a reference counter in the Persistent part of the objectis incremented. PARODY includes a pseudo delete operator named Destroy,which deletes the copied object only when there is only one copy remaining.

There are valid cases in which a program instantiates an object that isin use elsewhere in the program. In these circumstances, always instanti-ate the object with the new operator, do it from a try block, and providea catch handler that catches the Persistent" exception. Use the Destroyfunction to delete the object. Figure 8.15 is an example.

FIGURE 8.1 5 Processing multiple copies

void ProcessEmployee(EmployeeNumber en)

Employee *ep;

try f// --- instantiate the Employee

ep - new Employee(en);

Icatch(Persistent *obj) f

// --- the Employee is already instantiatedI/ --- use the existing copy

ep - obj;}

// process employee through ep

// ...Persistent: :Destroy(ep): // pseudo delete

}

Page 131: C++ Database Development 1558283579

Chapter 8: PARODY 121

Consider a function that processes two projects. The caller passes theproject numbers, and there are times when those two project numberscould be the same. Figure 8.16 shows how you would use the Persistent*exception to handle this condition.

void DoProjects(ProjectNumber pnl, ProjectNumber pn2)

{Project *projl - 0, *proj2 - 0;

try

fproji - new Project(pnl);

proj2 - new Project(pn2);

// values of pnl and

// pn2 might be the same

Icatch(Persistent *obj)

// --- processing the same object twice

proj2 - static cast<Project*>(obj);

// process the two Project objects ...// delete the two instances of Project objects

Persistent::Destroy(projl); // use Destroy instead of

Persistent::Destroy(proj2); // delete if multi copies

FIGURE 8.16 Processing multiple object copies

The Destroy function works whether the two pointers point to thesame or to different objects. Unlike the conditions illustrated inFigure 8.15, the two pointers in Figure 8.16 are in close proximity,and you could use the idiom shown in Figure 8.17 instead of callingDestroy.

delete progl;if (prog2 I- progi)

delete prog2;

FIGURE 8.17 Deleting multiple copies

Page 132: C++ Database Development 1558283579

1 22 C++ Database Development

The only problem with either approach occurs when the programdeclares an instance of the object before calling the DoProjects function.In that case, the catch handler executes before either pointer gets anaddress. Only proj2 would point to an object to process. The code thatprocesses the two objects would need to recognize that condition andbehave accordingly.

At first glance, it would appear that an overloaded delete operator forthe Persistent class could manage the reference counter and selective dele-tion. This does not work, however, because the delete operator does notexecute until all the object's destructors have executed. The object wouldbe effectively destroyed on the first call.

The Destroy function is a static member function of the Persistentbase class rather than a regular member function. It would seem that itcould have been a regular member function and that you could call it likethis:

ep->tDestroyn;

This usage puts the Destroy function in the position of having to performthis dubious operation:

delete this;

Most C++ programmers consider the statement just shown to be a dan-gerous practice. Therefore, Destroy is a static member function that takesas an argument the address of the object to be destroyed.

Persistent References to Persistent ObjectsSometimes a persistent object needs a perpetual reference to another per-sistent object. For example, if you have a Task class that gets much of itsmeaning from the Project class with which it is associated, you might findthat your application instantiates the parent Project object every time itdeclares a Task object. Rather than doing this every time yourself, you candefine the classes so that the Project object is a persistent reference in theTask class. Whenever you declare a Task object, the associated Projectobject is automatically declared, too. Figure 8.18 shows the Task class.

Page 133: C++ Database Development 1558283579

Chapter 8: PARODY 123

FIGURE 8.18 A referenced object

The Reference template class manages an object's reference to anotherobject. Its declaration in the referencing class establishes the reference.Figure 8.19 shows how the referencing class's Read and Write memberfunctions call ReadObject and WriteObject for the referenced object.Those calls take a different form than the others. The functions are calledthrough the referenced object rather than through the referencing objectfor which the Read and Write functions are running.

Until you do something to the contrary, the persistent reference isnull-no referenced object is instantiated when you instantiate thereferencing object. To assign another object to the persistent refer-ence, you use a simple assignment statement. The Task::SetProjectmember function in Figure 8.18 assigns the Project object argumentto the proj persistent reference. The argument must have been con-structed with the new operator, and you must delete it with thePersistent::Destroy function. Subsequent instantiations of the sameobject of the Task class automatically instantiate the referencedProject object. You do not need to delete that object. The referencingTask object does it for you.

The application can use the referenced object as shown in Figure8.20.

typedef Pit TaskNLIITber;class Task :public Persistent {

Key(TaskNumber> taskno; IIthe primary keystring name;Reference<Project> proj; referencedd objectvoid Reado;void Writeo;

public:Task(int tno);-Task( );void SetProject(Project& pr)

f proj - pr; I

I I ~ ~ ~l ! iio !i i~ • i ik ~ i i i i ••••••••••••)••••••••••••••••••••••••••••••••••••••••••••)••••••• •••••••••••••••••••••••••••••••••••••••••••••••••••••iii•i iiiiii ~¸i~iiii ii ii~ ) i iiiiii!•1 i i i~I;~ ! ) •~ '•••ii~i!iiii~i•~ •ii! i ii •i ••••• iL!••••! •• i!i 7; ;!i;i •i!i•i• !!!! !!¸•¸•i ! 7! !!•!• •!•L!•!!•!

Page 134: C++ Database Development 1558283579

1 24 c++ Database Development

FIGURE 8.1 9 Reading and writing object references

Task tsk(123);Project& pri - *tsk.proj.obj; ii referenced objectcout << "Hours expended on projectcout << prj.Hoursoi;

FIGURE 8.20 Dereferencing a persistent reference

The Reference<T> class has a public non-const pointer named obj thatpoints to the persistent object being referenced. You can use that pointerfor any access to the referenced object that its class permits. If youchange it and if you want the changes to be recorded in the databasewhen the referencing object goes out of scope, be certain to call theChangeObject function, discussed later. You can even delete the refer-enced object from the database by calling the DeleteObject function (alsodiscussed later), but if you do, you should remove its reference in the ref-erencing object. Figure 8.21 shows how you use the Reference<T>::RemoveReference function to do this.

void Task::Read()

TaskNumber tno:ReadObject~tno);taskno.SetKeyValue(tno);ReadObject(name);proj.Read~bjecto; 1/instantiate a referenced object

void Task: :Write()

WriteObject(taskno.KeyValueo):WriteObject(naine);proj.WriteObjecto; // store reference to the object

i i! ii ii i• i • ! !i !!! i ! i! ! i i • ii!~ • i i ii ~ ii• i l•i • ! ! ! i• i ! •! iiiii i¸i !¸• !i: ii ! i i !• •i !•ii•• ii ii !i !i!i!iiii i •~ i•!i•ii~ ~ii !i l liii iiiil ii i!ii i l ii~ ili l i i~ i ii i i ~ •iiii!i••i i i! i li iliii !!i i!ii! i~ • •! i¸i!•li ~ iiiii

Page 135: C++ Database Development 1558283579

Chapter 8: PARODY 125

Task tsk(123);

tsk.proj.obj->DeleteObjecto; // delete ref'd obj

tsk.proj.RemoveReferenceo; // remove reference

FIGURE 8.21 Deleting and removing a persistent reference

There is one more requirement for using Reference<T> objects in aderived Persistent class. The class being referenced must provide a conver-sion constructor with an ObjAddress parameter, as shown in Figure 8.22.

class Project : public Persistent f

publ i c:Project(ProjectNumber pn - 0);// --- constructor used by Reference<T> class

Project(ObjAddr oa)I LoadObject(oa); I

FIGURE 8.22 ObjAddr conversion constructor

The ObjAddr type is used by PARODY to store the logical databaseaddress of each object. This is the value that is stored in the referencingobject to point to the referenced object. You must provide this construc-tor so that PARODY can construct a persistent referenced object whenthe referencing object is instantiated.

Keep in mind that PARODY makes no relational integrity checks onpersistent references. If you delete a referenced object without removingits references in any other objects, PARODY still attempts to instantiatethe referenced object. The results are unpredictable.

Using the PersistentObject<T> ClassPARODY includes the PersistentObject<T> template. It parameterizespersistent objects and provides all the things that you normally provide

Page 136: C++ Database Development 1558283579

126 c++ Database Development

when you derive a persistent object from the Persistent base class. Youcan use it when your persistent object is a flat structure-no keys, point-ers, references, arrays, or virtual functions. Your application is responsi-ble for recording the Ob/Addr values for these PersistentObject<T>objects. Figure 8.23 shows how you can use the template.

FIGURE 8.23 Using the PersistentObject<T> template

How PARODY Brings It TogetherWith Key<T> and derived Persistent classes defined, the next thing to dois write a program that builds a database and declares and uses persistentobjects. This section is an overview of how PARODY works with yourclasses to make the objects persistent. You will revisit some of the proce-dures that this chapter has already discussed, this time from the view-point of how PARODY uses the procedures.

Page 137: C++ Database Development 1558283579

Chapter 8: PARODY 127

Opening the PARODY DatabaseThe first thing your program does is declare the Parody object just as theexample in Figure 8.14 does. This declaration opens the database if itexists and creates one if it does not. The database is identified by the filename that you provide in the Parody declaration.

Constructing a Persistent ObjectWhen you declare a persistent object, the sequence of constructors isimportant to how PARODY works. Consider a derived Persistent classsuch as the Project class in Figure 8.24.

class Project :public Persistent tKey<ProjectNumber> projno; iI primary keyKey<EmployeeNumber> manager; I secondary key

public:Project(ProjectNumber pn - 0);-ProjectC);

FIGURE 8.24 A persistent class

THE Persistent BASE CLASS

When you declare an object of the Project class in Figure 8.24, thefirst constructor that executes is the one for the base Persistent class.That constructor initializes the base member variables, and it initializes astatic global Persistent type pointer named Persistent::objconstructed topoint to the object being constructed.

THE KEYS

Next comes the PdyKey base class constructor for the promno object fol-lowed by the derived Key<ProjectNumber> constructor. The PdyKey con-structor uses the Persistent::objconstructed pointer to add its key objectto a list of keys associated with the persistent object.

The constructors for the manager key object execute next in turn, and

Page 138: C++ Database Development 1558283579

128 c++ Database Development

the manager key appends itself to the list. This sequence is what estab-lishes the projno object as the primary key. Its constructor executes first,so it is first on the list.

THE DERIVED Persistent CLASSAfter all the keys are constructed, the constructor for the derivedPersistent class executes. Figure 8.25 is an example of that constructor.

Project: :Project(ProjectNumber proj) :projno(proj)

LoadObjecto;

FIGURE 8.25 Constructor for a derived Persistent class

At a minimum, the derived persistent class constructor initializes the pri-mary key with a search value and the other data members with null val-ues. Then it calls the Persistent::LoadObject member function to load thespecified object from the database into the data members in memory.

THE Persistent::LoadObject FUNCTION

The first thing that Persistent::LoadObject does is clear thePersistent::objconstructed pointer so that any further construction of keyobjects does not try to associate the keys with the object in theprimary/secondary ranks. This measure is important because the indexingalgorithms make copies of keys for different reasons throughout the lifeof the object, and the key constructors need to know the difference.

LoadObject looks to see whether the program has already instantiat-ed a copy of the object. If it has, the function throws the Persistent"exception with the address of the existing object in the pointer that itthrows.

If no copy of the object has been declared, LoadObject searches forthe object in the database using the primary key value that the object'sconstructor initialized.

If the object exists in the database, LoadObject positions the PARODYfile system to where the object is stored. LoadObject does not know

Page 139: C++ Database Development 1558283579

Chapter 8: PARODY 129

enough about the object to load it. This is where the cooperation of thederived persistent class comes in. LoadObject calls the derived class's Readfunction so that it can specify the size of each data member and the addressinto which to read it.

If the object does not exist in the database, LoadObject records thatcondition in the object and returns, assuming that the calling constructorhas properly initialized the data members to null values.

THE Persistent::Read FUNCTIONThe derived class's Read function calls the base class's ReadObject func-tion for each of the data members. Figure 8.26 is an example of a persis-tent class's Read function.

FIGURE 8.26 A persistent class's Read function

Following the last ReadObject call, the Read function returns to theLoadObject function, which finishes up and returns to the object's con-structor.

THE Persistent::ReadObject FUNCTIONThere are several overloaded Persistent::ReadObject functions, one foreach of several data types. All of them reduce to a common functionthat has as its parameters an address and a character count. That func-tion uses the PARODY file input/output system to read the correctnumber of characters into the specified address. The function assumes

void Project::Read()t

ProjectNumber prno;

ReadObject(prno); /1 read the proj #

projno.SetKeyValue(prno); // put proj # in obj

ReadObject(name); // read the name

EmployeeNumber mgr;

ReadObject(mgr); // read the mgr's #

manager.SetKeyValue(mgr); // put mgr in obj

ReadObject(hours expended); // read hours

Page 140: C++ Database Development 1558283579

130 C++ Database Development

that the PARODY file system has already been positioned to the correctaddress. The read operation is not direct, however. A PARODY data-base is organized into fixed-length nodes. Each object occupies one ormore nodes in a linked thread, depending on the object's length. TheReadObject function reads data characters by following the object'sthread in the file. Appendix B describes PARODY's input/output systemof nodes.

Destroying a Persistent ObjectWhen you destroy a persistent object, the sequence of destructors is asimportant as the sequence of constructors was to the object's declaration.Consider the destruction of the Project class in Figure 8.24.

DESTROYING THE DERIVED Persistent CLASS

The first destructor executed is the derived class destructor. Figure 8.27 isthe destructor for the Project class.

FIGURE 8.27 Destructor for a derived persistent class

When the destructor begins to execute, nothing in the memory copy ofthe object has been destroyed yet. All the data members are intact. Thedatabase copy of the object, if one existed, has the same state it had whenthe program instantiated the object in memory. The destructor calls thePersistent::SaveObject function before it does anything else.

THE Persistent::SaveOblect FUNCTIONIf the object did not exist on the database when the program instantiatedit and if the program has specified that the object is to be added to thedatabase-a procedure that Chapter 9 addresses-the object must be writ-ten to the database. Likewise, if the object did exist and the program spec-ified that the data members had changed, the object needs to be written.

Page 141: C++ Database Development 1558283579

Chapter 8: PARODY 131

The Persistent::SaveObject function assigns a database address to anew object and adds its key values to the class's indexes. If the object ischanged, the SaveObject function positions PARODY's file system to theobject's address and updates any changed key values in the existingindexes. In either case, the SaveObject function then calls the derivedclass's Write function to write the data members.

If the object existed and the program specified that the object was tobe deleted, the SaveObject function releases the database file spaceowned by the object, deletes the key values from the class's indexes, anddoes not call the Write function.

THE Persistent::Write FUNCTIONThe derived class's Write function calls the base class's WriteObject func-tion for each of the object's data members. The sequence of calls mustexactly mimic the sequence of ReadObject calls in the Read function.Figure 8.28 is an example of a persistent class's Write function.

void Project:8:Write(iI

WriteObject(projno.KeyValueo); Write pron #WriteObject(nme); w p write nameWriteObject(,h anager.KeyValueo); o write mgr's #Writeflbject(hoursexpended); write Oours

I

FIGURE 8.28 A persistent class's Write function

Following the last Write Object call, the Write function returns to theSaveObject function, which pads and writes the last node owned by theobject, finishes up, and returns to the object's destructor.

THE Persistent:: Write~bject FUNCTIONThere are several overloaded Persistent:: Write Object functions, one foreach of several data types. All of them reduce to a common function thathas as its parameters an address and a character count. That functionuses the PARODY file input/output system to write the correct number ofcharacters from the specified address. The function assumes that thePARODY file system has already been positioned to the correct address.The WriteObject function writes data characters by following the object'snode thread, growing new nodes if necessary.

Page 142: C++ Database Development 1558283579

132 c++ Database Development

DESTROYING THE KEYS

Destructors for the Key<T> objects execute after the derived persistentclass's destructor, with the derived Key<T> destructor executing ahead ofits base PdyKey class destructor.

DESTROYING THE Persistent BASE CLASS

The base Persistent class destructor executes last. It cleans up some tablesbuilt by the base class and returns. The object is now destroyed fromwithin the program and is properly restored to or deleted from the data-base as appropriate.

Object Input/Output: An OverviewFigure 8.29 summarizes the flow of function calls to load and save anobject.

INPUT OUTPUT

basePersistentclass

derivedobjectclass,

FIGURE 8.29 Object persistence flow

To load an object, the derived class's constructor calls the LoadObjectfunction in the base Persistent class, which calls the Read function in thederived class, which in turn makes several calls to the ReadObject func-tion in the base class.

To save an object, the derived class's destructor calls SaveObject inthe base class, which calls Write in the derived class, which in turn makesseveral calls to WriteObject in the base class.

Your responsibilities in persistent class design are to provide thederived class's constructor and destructor and its Read and Write func-tions. None of these functions needs to understand the complexities ofthe database file structures. Their responsibilities are focused on the for-mat of the object itself.

INPUT OUTPUTI

Page 143: C++ Database Development 1558283579

Chapter 8: PARODY 133

Integrity ChecksChapter 9 describes how the using program calls the AddObject,ChangeObject, and DeleteObject functions to tell the Persistent baseclass how to handle the object in the SaveObject process. When thosefunctions are called, they decide whether the object, as it is configured atthat time, maintains or violates class relationship integrity.

The AddObject and ChangeObject functions test each of the sec-ondary keys in the object. If the key is related to another persistent class,the functions search the related class in the database for an object thatmatches the value of the key. If no match is found, the functions refuse toset the flags that tell the SaveObject function to write the object to thedatabase when it is being destroyed.

The DeleteObject function tests to see whether any other classes arerelated to the class of the object being deleted. If they are, the functionsearches those classes to see whether other objects are related to the onebeing deleted. If they are, the function refuses to set the flag that tellsSaveObject to delete the object.

In all three cases, when the functions refuse to do what the program hasrequested, the functions return a false value that the calling program can test.

It is important to understand the timing of these integrity checks.Suppose that you call one of the three functions, which approves the add,change, or delete. Then you modify the object or the database so that theaction would not have been approved. The action occurs anyway.

SummaryThis chapter described PARODY and how you define a PARODY data-base. It discussed how PARODY implements your database in cooperationwith the classes you design. Chapter 9 is about how an application uses aPARODY database to create, modify, and delete objects. You will alsolearn in Chapter 9 an alternative to PARODY's almost-relational datamodel. In that model, you can retrieve objects by their logical addressrather than by key values-navigational rather than associative access.

ReferencesAdvanced C++ Programming Styles and Idioms, James 0. Coplien, 1992,Addison-Wesley

Page 144: C++ Database Development 1558283579

Chapter 9

Using a PARODY DatabaseAn object in possession seldom retains the same charmthat it had in pursuit.

-Pliny the Younger

This chapter is about how programs use the object database. Chapter 8described how you define the database by designing properly inheritedclasses. In this chapter you will learn how to declare persistent objects,manipulate them, and tell PARODY what it should do about them.

Building a DatabaseYou build a new PARODY database the first time you declare an objectof the Parody type. The string name that you give the database becomesthe file name for the object data and index files. PARODY adds the fileextensions ".dat" and ".ndx" when it creates the files.

Declaring the Parody object opens the database for use by your pro-gram. The database remains open as long as the Parody object is inscope.

135

Page 145: C++ Database Development 1558283579

136 c++ Database Development

You can have more than one database open at the same time. If youare using objects from multiple databases, the derived Persistent con-structors must specify in their member initialization list which databasean object is stored in. You need to make the address of the Parody objectknown globally so that the constructors can associate themselves with thecorrect database. If an application uses only one database, the construc-tors may omit the reference to the database, and the database object doesnot need to be global.

Managing Persistent ObjectsWith an open PARODY database you can add persistent objects, retrievethem, change them, and delete them. PARODY maintains the integrity ofthe interclass relationships, and you can monitor its actions in thisregard. You can navigate among the objects of a class and among relatedobjects of different classes. The examples that follow assume that you areusing the Employee class shown in Figure 9.1.

FIGURE 9.1 An example persistent class

typedef int EmployeeNumber;

typedef int DepartmentNumber;

class Employee : public Persistent {

Key<EmployeeNumber> emplno;

Key<DepartmentNumber> deptno;

public:

Employee(EmployeeNumber en = 0);-Employee);

Key<DepartmentNumber> *DeptKey()

t return &deptno; ISetDeptNo(DepartmentNumber dn)

( deptno.KeyValue() - dn; }DepartmentNumber DeptNo() const

t return deptno.KeyValueo; 1// ...

II;

Page 146: C++ Database Development 1558283579

Chapter 9: Using a PARODY Database 137

Working with KeysThe class in Figure 9.1 has two keys. By default, the first key is the pri-mary key for objects of the class, and all others are secondary keys. Thekeys are parameterized types of the Key<T> template class, which buildssingle-element keys. Concatenated keys are built with theCatKey<T1,T2> template class.

SINGLE-ELEMENT KEYS

A program needs to assign values to keys and read those values back.You have seen some of that in the discussion on the persistent object'sRead and Write functions. Figure 9.2 shows how the persistent class'sconstructor places initial values in the keys.

FIGURE 9.2 Initializing key values

The constructor in Figure 9.2 initializes the emplno key with theEmployeeNumber parameter and the deptno key with zero. Programscan subsequently assign values to keys with the SetKeyValue function asshown in Figure 9.3.

deptno.SetKeyValue(101);

FIGURE 9.3 The SetKeyValue function

The example in Figure 9.3 shows how a member function of the per-sistent class would change the key value. Inasmuch as the key is a pri-vate data member, the Employee class includes the public SetDeptNofunction to allow a user to set the class's department number keyvalue.

The program retrieves a key's value by calling its KeyValue functionas shown in Figure 9.4.

Employee::Employee(EmployeeNumber en) : emplno(en), deptno(O)t

// ..ii

Page 147: C++ Database Development 1558283579

138 c++ Database Development

DepartmentNumber dn - deptno.KeyValueo;

FIGURE 9.4 The KeyValue function

The example in Figure 9.4 shows how a member function of the persis-tent class would retrieve the key value. Inasmuch as the key is a privatedata member, the Employee class includes the public DeptNo function toallow a user to retrieve the class's department number key value.

CONCATENATED KEYS

Concatenated keys consist of two key values and use the CatKey<T1,T2>template. Figure 9.5 shows this usage.

FIGURE 9.5 A class with a concatenated key

A concatenated key consists of two key values that combine to form aprimary key and that operate individually as secondary keys. Figure 9.6shows the construction of a class with a concatenated key.

FIGURE 9.6 Constructing concatenated keys

Concatenated keys have functions named Keyl and Key2 that return ref-erences to the two Key<T> objects of the concatenated key. You can usetheir KeyValue and SetKeyValue functions, or you can use the

class Assignment : public Persistent {CatKey<Empl oyeeNumber,Departmentt.umber> assi gnment;

public:Assignment(EmtployeeNumber en -0, DepartmentNumber pn -0);

Assignment: :Assignhient(EmployeeNumber en, ProjectNumber pn)assignment(en, pn){iiii! ~i,•m ~ ii !i•• i!• ¸¸ !iii••••! ii•¸ ••¸¸•iii•• ii¸¸• i•~i¸•¸¸¸ ¸¸¸ ¸•• •i :•i•o i iNu•!•i i ~~i!?••••• iiien~•i •• i•iii•i• ii ii• % ii~•i••ii •:: !i~i! !ii~••i!iiii•!i!i~••i !i..

1 / . ..!i!ii~i~,,~~~~l••J!•!•J iii••i•i~ ~~• iiJ!••••!!••!! i••iiJ!!••ii•!!i!!!iii••ii!!i•!•!!•!i•~ ! ! i i ! !! !ii~~iip i~• ; !i! i•ii!!i!!!i•~ ~~•••i ! ii!!•ii !i!!!i•~ •i••~•iiii•ii•i ,II i !• i!i!ii!ii~ i!'• iii !iii!!ii!ii~ i• i ~ ~•i• !ii!•~!ii !i!!!ii• i~~!ii•!ii!• ...... ... .. ..... ... ... .. .. ..ii !!i !i~•!iii~ !ii~ ~ii !i !ii~!i~ ~ii~ ~l! i! !• !ii!! !'iii• •i•!i!ii,!iiii~ i• !l i~ ~~~i•!ii!~i!i• 1!!i••~ii~ i i!! ,! !~• !i~ ~ !!i,!•ii ! i•!i¸ ! i!••!•i i

Page 148: C++ Database Development 1558283579

Chapter 9: Using a PARODY Database 139

KeyValuel, SetKeyValuel, KeyValue2, and SetKeyValue2 functions of theconcatenated key itself.

Retrieving Persistent ObjectsYou retrieve a persistent object by declaring one that exists in the data-base. Figure 9.7 shows this procedure.

Employee empi (l23)if (empl.ObjectExistso) 11

II--- the object exists

FIGURE 9.7 Retrieving o persistent object

The empl object in Figure 9.7 is constructed from the 123 primary keyvalue, which is the employee number in this example. The ObjectExistsfunction returns a true value if PARODY found a matching object on thedatabase. In either case, the object is instantiated.

If the object does not exist on the database, the object's memory copycontains the key value that you specified. The other data members areinitialized to whatever values are assigned by the derived Persistent objectconstructor-in this case the constructor for the Employee object. ThePersistent base class has initialized its data members to indicate that theobject is not on the database.

If the object exists on the database, PARODY will have read its datamembers into its memory copy by calling the derived Read function thatyou provide in the class design.

After an object is instantiated, existing or not, the program may ormay not change the object's data values and then call AddObject,ChangeObject, or DeleteObject as described later in this chapter. Theprogram can also decline to call any of those functions allowing theobject to go out of scope without modifying the database.

Creating Persistent ObjectsYou create a persistent object by declaring one that does not already existon the database and then telling PARODY to add it to the database.Figure 9.8 shows this procedure.

Page 149: C++ Database Development 1558283579

140 C++ Database Development

FIGURE 9.8 Adding a persistent object

The empl object in Figure 9.8 is constructed from the 123 primary keyvalue, which is the employee number in this example. This time, PARODYdoes not find a matching object, so the program calls the AddObject func-tion to tell PARODY to add the object to the database. Calling AddObjectdoes not immediately write the object to the database. It tells PARODYthat the program intends to add the object to the database when the objectgoes out of scope. You can continue to make changes to the object; they arewritten to the database when the object goes out of scope.

Changing Persistent ObjectsTo change a persistent object, declare one that exists, change its data values,and call the ChangeObject function. Figure 9.9 is an example of that process.

Employee empl (123);if (empl.ObjectExistso) t

/ -- the object exists1/... changee the data members)..

empl.,CharigeObjecto:

FIGURE 9.9 Changing a persistent object

Calling ChangeObject does not immediately write the changed object tothe database. It tells PARODY that the program has made modificationsand intends to write the changed object to the database when the objectgoes out of scope. You can continue to make changes to the object aftercalling ChangeObject. Those changes are written to the database alongwith any others when the object goes out of scope.

Page 150: C++ Database Development 1558283579

Chapter 9: Using a PARODY Database 141

Deleting Persistent ObjectsTo delete a persistent object, declare one that exists and call theDeleteObject function. Figure 9.10 is an example of that process.

Employee empl (123);if (empl.ObjectExistso) {

// --- the object exists

empl .DeleteObjecto;

FIGURE 9.10 Deleting a persistent object

Calling DeleteObject does not immediately delete the object from thedatabase. It tells PARODY that the program intends to delete the objectwhen the object goes out of scope. You can continue to work with theobject in memory; the delete does not take effect until the object goes outof scope.

IntegrityThe AddObject, ChangeObject, and DeleteObject functions return a truevalue if they permit the action and a false value if they do not. The func-tions do not permit the action if it would violate class integrity. AddObjectand ChangeObject reject an object that attempts to relate itself to anotherobject when the other object does not exist on the database. DeleteObjectrejects the deletion of an object if other objects in the database are stillrelated to it. Figure 9.11 shows how those functions work.

if (!empl.AddObjecto)// --- add was rejected

if (!empl .ChangeObjecto)// --- change was rejected

if (!empl.DeleteObjecto))// --- delete was rejected

FIGURE 9.11 Maintaining object integrity

Page 151: C++ Database Development 1558283579

142 c++ Database Development

These return values reflect the condition of the database when the func-tions execute. Nothing prevents the program from making further changesto the objects or to the rest of the database after the integrity tests arepassed. Therefore, if you depend on return values from these functions toconvey the condition of the database's integrity, make these function callsthe last thing you do to the object before it goes out of scope.

Navigating the DatabaseYou can navigate a database two ways: by associative access, which useskey index values to address objects, or by navigational access, which usesthe addresses of the objects. The first method is relational, and the sec-ond reflects a more object-oriented approach.

Navigating by Key: Content-Addressable ObjectsThe relational data model uses key index values to locate objects.Although it is not a relational database management system, PARODYsupports this technique. You have already seen that a program canretrieve a persistent object by instantiating it with a primary key value.You can find objects by using the values of the secondary keys as well.This facility permits the kind of content-addressable access normallyassociated with relational databases.

FindObjectThe FindObject function locates an object by using the value assigned bythe program to a secondary key. Figure 9.12 shows how that works.

FIGURE 9.12 Finding an object with a secondary key

Page 152: C++ Database Development 1558283579

Chapter 9: Using a PARODY Database 143

In this example, the program instantiates an Employee object with no speci-fied primary key value. The default constructor for the class builds the objectwith a null key value, and PARODY does not try to find a matching object inthe database-thus the importance of the isNullValue function described inChapter 8. The empl object has no meaningful data member values yet. Theprogram assigns one data value-the department number-which, in thisexample, is a secondary key. The program now tells the object to find the per-sistent object by using the initialized value in the secondary key.

The FindObject function, which accepts a pointer to a PdyKey class,does the retrieval. The PdyKey class is the base class for all Key<T> andCatKey<T1,T2> parameterized types. In this example, the DeptKey func-tion returns the address of the class's Key<DepartmentNumber> deptnodata member.

If the database has at least one Employee object with a matchingentry in its secondary department number key, PARODY initializes theempl object with the first such Employee object. The sequence of objectsof the same type with identical values for a particular secondary key iscoincidental. The call to FindObject retrieves the first coincidental one.

FirstObject AND LastObjectYou can retrieve the first or last object in a primary or secondary keysequence by using the FirstObject and LastObject functions. These func-tions do not require that you initialize the key data value. They retrievethe objects from the beginning or end of the key sequence. Figure 9.13 isan example of using these functions.

Employee empl ;empi . FlrstObject(eiupl .DeptKeyo);if (empl.ObjectExistso){

If--- the object exists

empl.LastObject(enipl ADeptKeyo);if (empl.ObjectExistso) t

II--- the object exists

FIGURE 9.13 Finding the first and last abject of a class

Page 153: C++ Database Development 1558283579

144 C++ Database Development

These functions return false only if there are no objects of the derivedPersistent class in the database that have non-null values in the secondarykey. If you have specified a primary key, the functions return false only ifthe database has no objects of the derived Persistent class whatsoever.

If you call the FirstObject and LastObject functions without passinga pointer to a key, PARODY finds the first or last object in the sequenceof the primary key. Figure 9.14 illustrates this usage.

FIGURE 9.14 First and last object by primary key

The same convention applies to NextObject and PreviousObjectdescribed next.

NextObject AND PreviousObjectThe NextObject and PreviousObject functions return the next and previ-ous sequential objects in the sequence of the key through which they arecalled, or through the primary key if they are called through the persis-tent object itself. These functions assume that either they, FirstObject,LastObject, or FindObject has positioned the key at an object. If not,NextObject works just like FirstObject, and PreviousObject works justlike LastObject. Figure 9.15 illustrates their use.

This navigation uses the same object in memory to hold each of thesuccessive retrieved objects. As long as there is a next or previous objectin the key sequence, the object's ObjectExists function returns a truevalue. After NextObject has retrieved the last object and you call it

Employee empi ;empi .FirstObjecto;if (empl.ObjectExistso) f

II--- the object exists

empi LastObjecto;if (empl.ObjectExistso) I

/- - - the object exists

Employee empl;empl.FirstObject();

if (empl.ObjectExists()) {

// --- the object exists

.// ...}// ....empl.LastObject():

if (empl.ObjeetExists())

// --- the object exists// ...

}

Page 154: C++ Database Development 1558283579

Chapter 9: Using a PARODY Database 145

again, it does not retrieve another object and the ObjectExists functionreturns a false value. Likewise, ObjectExists returns false afterPreviousObject has gone past the first object in the key sequence.

FIGURE 9.1 5 Navigating forward and backward

Each key in a persistent class object keeps track of its own logical posi-tion in the thread of objects in its sequence. You could conceivably haveseveral threads running at one time by doing retrievals on the same mem-ory object with several of its keys or by declaring more than one instanceof the class and running retrievals on all of them. Keep in mind, however,that if you try to instantiate the same object more than once, PARODYthrows the Persistent* exception.

NAVIGATING AMONG CLASSES

Sometimes a program must declare a persistent object and then retrieve,one by one, its related objects of another class. For example, Figure 9.16retrieves the employees assigned to a particular department.

The program in Figure 9.16 instantiates a Department object with321 as its primary key value. Next, it instantiates an empty Employeeobject, sets the same department number into that object's secondary key,and calls FindObject using the secondary key for the retrieval. The pro-

Page 155: C++ Database Development 1558283579

146 C++ Database Development

gram processes that employee object and subsequent employee objectsretrieved by the NextObject function until either the department numberin the next employee object changes or the ObjectExists function returnsa false value, indicating the end of the Employee class's department num-ber key sequence.

FIGURE 9.16 Navigating among classes

Navigating by Address: Position-Addressable ObjectsWe have stressed until now the strength of the relational approach todatabase navigation. There are times, however, when keyed indexes arenot the best solution for database access and navigation. Sometimes youjust want to retrieve a persistent object by using some kind of handle thatthe database manager provides when you create the object.

Perhaps you are using the object database to retain some number ofsingle objects between invocations of a program or for passing the objectfrom program to program. For example, a CAD/CAM database mightcontain one object each of several classes, among them the design model,some number of canned features, and perhaps some embedded textstrings. The application does not need to retrieve objects from a collec-tion on the basis of data values. It needs to retrieve the one and onlyobject of a class or one of a very few objects of a class. This is a designdecision, and you should know whether this data model applies to yourapplication. As a general rule, if the database stores a large number ofobjects of the same class, the objects should be indexed by a key value. Ifthe database stores a small number of objects of each class-small

Page 156: C++ Database Development 1558283579

Chapter 9: Using a PARODY Database 147

enough that the application does need to address them by unique datavalues-then the objects may be keyless.

PARODY supports the design of a database without keys and theretrieval of objects without indexing.

DESIGNING A CLASS WITHOUT KEYS

To design a class without keys, simply leave out the keys. If no data mem-ber is built with the Key<T> or CatKey<T1,T2> class, then the objects arekeyless, and you must retrieve them by specifying their logical PARODYaddresses. The class still has its Read and Write functions, the constructorstill calls LoadObject, and the destructor still calls SaveObject.

The keyless class must recognize two constructors: one that allows itto be declared with no data values, and one that allows it to be declaredwith an ObjAddr parameter. Both constructions may be provided by oneconstructor that uses a default parameter value to set the ObjAddr para-meter to zero. The constructor's call to LoadObject includes the ObjAddrparameter's value as a parameter. Figure 9.17 is a simple keyless classthat stores a text string for an application.

class Text : public Persistent

string text;void Read()

f ReadObject(text); Ivoid Writef)

t WriteObjIect(text); ipublic:

Text(const string& str) text(str)t LoadObjecto; I

Text(ObjAddr objaddr - 0)t Load~bject(objaddr); J

-Text()f Save~biecto; J

const string& GetText()t return text;

FIGURE 9.17 A keyless class

Page 157: C++ Database Development 1558283579

148 c++ Database Development

CREATING A KEYLESS OBJECT

You create a keyless object by instantiating it, assigning values to its datamembers, and calling AddObject just as you do with keyed objects.There are no integrity checks with keyless objects because there are nokeys to relate classes.

After the call to AddObject you must get the logical PARODYaddress of the new persistent object. The ObjectAddress functionreturns the address of an object once it has been added to the data-base. It is the responsibility of the application to remember the addressfor subsequent retrievals of the object. Figure 9.18 creates an object ofthe Text class.

Text tx("Birth of the Cool");tx.AddObject);ObjAddr oa - tx.ObjectAddresso;

FIGURE 9.18 Creating a keyless object

RETRIEVING BY OBJECT ADDRESS

To retrieve a keyless object, you instantiate it with its object address as aparameter. Figure 9.19 retrieves the Text object from the database bypassing the object's address to its constructor.

void f(ObjAddr objadr){

Text tx(objadr);cout << tx.GetTexto;

FIGURE 9.19 Retrieving a keyless object

The example in Figure 9.19 assumes that the caller passes the correctobject address to the function, which declares an object from the address.If PARODY determines that the address does not point to an object ofthat class, it does not call the class's Read function, and ObjectExists, ifyou call it, returns false. This condition would probably be a program

Page 158: C++ Database Development 1558283579

Chapter 9: Using a PARODY Database 149

bug rather than a user error, because the technique assumes that the pro-gram can always know the correct addresses for the objects it retrieves.

NAVIGATING BY OBJECT ADDRESS

You can use the FirstObject, LastObject, NextObject, andPreviousObject functions for a keyless class. These functions navigate theclass in the coincidental physical sequence of the objects of the class inthe database. This sequence is not necessarily the sequence in which theobjects were created. The algorithm that allocates space in the databasefor new objects reuses deleted object space, so an object that you createdyesterday might properly be found after one that you create today.

This form of navigation can be inefficient in a large database. PARODYmaintains no pointers to or chains of keyless objects. When you callFirstObject, for example, PARODY scans the database from the beginninglooking at every node to find the first node of the first object of the class.The other scans work similarly, scanning from the last node of the file orfrom the address of the object through which you call the function.

UPDATING AND DELETING KEYLESS OBJECTS

Updating and deleting keyless objects involve retrieving the object to bechanged or deleted, changing the data members for a change, and callingChangeObject or DeleteObject just as you do with keyed objects. Thereare no integrity checks other than to make sure that the object addresspoints to an object of the class being changed or deleted.

Navigating ShorthandEach of the navigating functions-FindObject, FirstObject, LastObject,NextObject, and PreviousObject-returns a reference to the object forwhich it was called. This convention allows you to use shorthand nota-tion such as that shown in Figure 9.20.

Department dept;if (dept.FirstObjectoO42bjectExistso)

FIGURE 9.20 Navigating shorthand

Page 159: C++ Database Development 1558283579

150 C++ Database Development

Converting PARODY DatabasesA database that has been designed, implemented, and placed in use is notnecessarily a fixed entity. Requirements for the application change, andfrom time to time you need to modify the format of an existing database.Such modifications imply a conversion of the existing database to thenew format. There are several things to consider.

Adding ClassesIf you are adding one or more classes to the database, your conversion issimple. Make no physical conversion of the database files at all. Designthe classes, instantiate objects, and add them to the database. The data-base accepts the new classes without modification.

Removing ClassesTo remove a class, delete all the objects. You do not need to make a phys-ical conversion of the database file, and you can later reuse the classname for a new class.

Adding Data Items to an Existing ClassYou can add data items to an existing class without converting the data-base if the following conditions are true. First, you must add the dataitems at the end of the series of calls to ReadObject and WriteObject fromyour Read and Write functions. Second, your constructor should initializethe new data items to zero values before it calls ReadObject. Third, thenew data items should behave appropriately when they have zero values.

The ReadObject function does not read past the last node of anobject. The last node of an object is padded with zeros. Therefore, if youinitialize the additional data items with zeros and if ReadObject eitherreads padding bytes into the data item or reads nothing because there areno more nodes in the object's node string, then the data item containszeros after the persistent object is instantiated.

The WriteObject function allows new or changed objects to grow totheir proper lengths.

Page 160: C++ Database Development 1558283579

Chapter 9: Using a PARODY Database 151

Removing Data Items from an Existing ClassIf you remove a data item from an existing class, you should convert thedatabase as described later in this section. You can avoid that conversionif the Read and Write functions continue to allow for the data item'sspace by reading and writing to and from unused variable space. In prac-tice, however, you will find that such workarounds are inefficient andcloud the design. Residual data values remain in the database in oldobjects, and new or changed objects have useless holes. It's better to do aproper job and convert the database.

Adding Keys to an Existing ClassIf you are changing an existing data item into a key, you might notneed to convert the database as long as the data item's physical sizeand proximity to other data items in the class do not change. Replacethe data item in the class definition with one parameterized by theKey<T> class. Then rebuild the indexes for the database as describedin a later section.

If you are adding a completely new key data item, use the proce-dures for adding any data item, add the Key<T> object to the class,and rebuild the indexes for the database as described later in thischapter.

Converting the DatabaseThe changes you make will most likely affect only a small part of thedatabase, perhaps only one class. You need to convert that class only,and you can do it within the database itself. Leave the original classintact and use a dummy name for the replacement class. For example, ifyou are changing the Department class, add the new class with thename DepartmentNEW. The conversion consists of the following sixsteps.

STEP 1Design the DepartmentNEW class to reflect the changes you are making.Add the class design to the database schema. Leave the originalDepartment class design in the schema.

Page 161: C++ Database Development 1558283579

152 C++ Database Development

STEP 2Write a conversion constructor to convert the Department class to theDepartmentNE W class.

STEP 3Write and run a conversion program that reads all the Department objects,converts them to DepartmentNEW objects, and deletes the Departmentobjects. Figure 9.21 is an example of such a conversion program.

FIGURE 9.21 A class conversion program

STEP 4Rename the DepartmentNEW class to Department and replace the oldDepartment class definition in your source code header files.

STEP 5Make all appropriate modifications to the application that the changeddatabase requires. Compile and link the entire application with the newclass definition.

Page 162: C++ Database Development 1558283579

Chapter 9: Using a PARODY Database 153

STEP 6Rebuild the database indexes as described below. This step removes theDepartmentNEW class identification from the index file and associatesthe Department class identification with the new modified class.

Rebuilding the Index FileThe weakest link in a relational database is the integrity between theindexes and the data files. If a system error causes an index to pointsomewhere other than to the object it indexes, the database is in a stateof disintegrity and is unstable and probably unusable. In other errors, theintegrity of the index file itself can be disturbed.

Database Integrity ErrorsThese errors usually occur when the system fails while the indexes andobjects are being changed. Until the changes are completed, the system isunstable. The failure causes the instability to remain when you restart thesystem. The symptom of such instability is the inability of the applicationto retrieve valid and logical objects. Therefore, the first thing that a data-base user should try when those symptoms occur is rebuilding the index-es. You should provide such a program for each database that you build.As you learned in the previous section, database conversion tasks needutility programs to rebuild indexes.

The Index Rebuilding ProgramEach database needs its own program that rebuilds indexes. The programis not complex, and Figure 9.22 is an example of such a program.

The example conversion program in Figure 9.22 rebuilds the indexesfor a database that contains objects of two classes. The program getsPARODY's internal class identification codes for the classes in the data-base by calling the GetClassID function and passing the names of theclasses.

The program scans all the nodes from number 1 to the highest nodeused. It reads each node's object header to see whether the node is the firstnode of an object. If so, the program calls the RebuildIndexes function to

Page 163: C++ Database Development 1558283579

154 C++ Database Development

tell the database to rebuild the indexes to point to the current node for thenext persistent object instantiated. Then the program instantiates an objectof whatever type the object header indicates. PARODY and the object readthe data values from the database through the Read and ReadObject func-tions invoked by the object's constructor. PARODY does not try to find theobject from its index value because the program told it that it was rebuild-ing indexes. Instead, PARODY uses the node number that the programpassed to the RebuildIndexes function to position the database to read thedata values. When the object goes out of scope-which it does right away-PARODY rebuilds the object's indexes from the data values in the keys.

FIGURE 9.22 An index rebuilding program

#include <stdio.h>#include "personel.h" /1application-specific header#define dbname 'TERSONEL"void Buildlndex()II

remove(dbname ".ndx"); // delete old index fileParody *personnel - new Parody(dbnaine):ObjectHeader objhdr:ClassIO EMPLOYEE -

personnel->GetClassIO~typeid(Employee).nameC)):ClassID DEPARTMENT =

personnel ->GetClassID(typeid(Department) nameeo:tNodeNbr nd 1;NodeNbr end -personnel->datafile.HighestNodeo;

1//--------scan Parody nodeswhile (nd <- end) (

// - -- read the object header for this nodepersonnel->GetObjectl~eader(nd, objhdr);/1 - -- object relative node# 0 is Ist node of objectif (objhdr~ndnbr - 0) f

// --- tell Parody to rebuild indexes this objectpersonnel ->Rebuildlndexes(nd);1//------rebuild depending on class typeif (objhdr.classid - EMPLOYEE) t

Employee empl;I

Page 164: C++ Database Development 1558283579

Chapter 9: Using a PARODY Database 155

FIGURE 9.22 Continued

The program's main function does not do the work. It calls theBuildIndex function, which is the name that you must use. That functionname is a C++ friend of the Parody class, which gives it access to theGetClassID, RebuildIndexes, and GetObjectHeader functions and to thedatafile object to determine when the scan of the database has reachedthe end.

SummaryThis chapter taught you how to use PARODY's persistent objects, how toperform database conversion, and how to rebuild database indexes.Chapter 10 presents case studies that are examples of PARODY applica-tions. It also introduces some utility classes that you can use as data typesto build data members in your persistent objects.

else if (objtwir.classid -DEPARTMENT)

Department dept;

delete personnel;

I

Build Indexo);return 0;

I

Page 165: C++ Database Development 1558283579

Chapter 10PARODY in Applications

Few things are harder to put up with than the annoyanceof a good example.

-Mark Twain

This chapter presents four small working applications that use PARODY.You can use these examples to learn more about how PARODY worksand to guide you when you develop your own applications.

The first example uses keyless objects to store and retrieve simplepayroll records. It demonstrates how the PersistentObject<T> templateclass encapsulates the persistence of simple, flat structure objects.

The second example implements a classic computer simulation, theGame of Life, using a string key to store and retrieve Life patterns.

The third example, a personnel management system, uses the almost-relational properties of PARODY to implement a personnel managementsystem that maintains indexed objects for departments, projects, employ-ees, and assignments. You will recognize much of the personnel applica-tion from the examples in earlier chapters. This example illustrates per-sistent object inheritance.

The fourth example maintains a family tree database. It illustratesthe use of persistent reference objects in a PARODY application.

Before discussing the example applications, this chapter addressessome supporting software. PARODY uses several general-purpose data

157

Page 166: C++ Database Development 1558283579

158 C++ Database Development

structure classes that you might find useful in your own applications.This discussion also describes GUI, the generic user interface class thatthe example applications use.

Note: Please realize that the programs in this chapter are notintended to be models of user interface excellence. Their pur-pose is to show how to build applications that store, retrieve,and maintain persistent objects in a PARODY database. Theyare kept simple in the interest of brevity and to avoid the dis-tractions that come from cluttered code examples. Your use ofthese techniques will, I hope, occur within a more comprehen-sive application framework than the one used here.

The source code for these classes and the example applications is inAppendix C. This chapter refers to them by their source code file names,and the discussion of each example application begins with a list of thepertinent files. A table of contents at the beginning of the appendix tellsyou where to find each of the listings. You should read the discussionswhile looking at the files that they address.

Utility ClassesThe following source files are discussed:

* bool.h+ linklist.h* money.cpp* money.h* date.cpp

* date.h

PARODY defines several utility classes for use in building persistentobjects. The first class, bool, emulates the Boolean type that the ANSIcommittee is considering for inclusion in standard C++. The second class,LinkedList<T>, is a template class that implements a doubly linked listdata structure to manage lists of objects. PARODY uses the LinkedListclass internally to maintain lists of keys and objects. The other two class-es-Money and Date-are used by the example applications as user-defined data types.

Page 167: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 1 59

The string ClassThe previous version of PARODY included a String class. The ANSIcommittee has proposed a standard string class, which Borland compilersimplement. PARODY now uses the ANSI string class instead of its own.Current developments in the committee indicate that the string class defi-nition might soon use templates to support strings with internationalcharacter widths. PARODY's use of strings might necessarily have tochange at some time in the future whenever the standardization activitysettles down.

The bool ClassThe ANSI committee has proposed a bool data type to implementBoolean-two-state-objects with true and false values, and automatictype conversions with integer objects that have nonzero and zero values.The definition is not approved as of this writing, but its details are gener-ally understood. The bool class implemented in bool.h emulates the ANSIbool data type. An application program uses the bool type as shown inFigure 10.1.

FIGURE 10.1 Using the bool doto type

You can compare a bool object to an integer. The comparison reflects therelative truth of the two objects rather than the numerical relationship.When you assign an integer to a bool object, the assigned value is con-verted to true or false depending on the nonzero/zero value of the integerbeing assigned.

Note: After the ANSI bool type is implemented, bool becomes aC++ keyword. When and if that happens, the declarations in

#i ncl ude "bool.hbool PgmSwitch - true; // or false

if (PgmSwitch)

if (!PgmSwitch)1/M.,

Page 168: C++ Database Development 1558283579

160 C++ Database Development

bool.h will not compile with contemporary compilers. Simplyeliminate the preprocessing directives that include bool.h, and,if my assumptions about the implementation of bool are cor-rect, the programs will compile and execute properly.

LinkedList<T>PARODY uses linked lists in several places. A linked list is a datastructure that links objects in a list that the program can navigate. Alinked list consists of a list head that points to the first and lastobjects in the list and list entries that are themselves the listedobjects. Each list entry includes its own data values, a pointer to thelist head, and pointers to the next and previous entries in the list. Aprogram can append entries to the list, insert an entry at a specifiedposition relative to another entry, delete entries, and navigate the listforward and backward starting at either end or at a chosen entry inthe middle of the list. Figure 10.2 illustrates the architecture of alinked list.

LinkedListHead LinkedListEntries

First DataI I ~I

Last * DataDta

IPrey N~ex~t

I Prev J Next II I

Data Prevy Next

FIGURE 10.2 Linked list architecture

No doubt the ANSI committee will approve a standard linked list class.Until then, PARODY uses the one defined as a template in linklist.h. Youcan use the template yourself in your programs by declaring objects oftype LinkedList<T> as shown in Figure 10.3.

I

I

Page 169: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 161

FIGURE 1 0.3 Using the LinkedList<T> class

There are member functions to insert and remove entries from the list basedon entry position or relative to a specified other entry. You can retrieve anentry based on its position in the list. Appendix A includes a full referenceguide to the LinkedList<T> template class and its member functions.

The Date ClassThe Date class defined in date.h and date.cpp implements a simplemonth/day/year structure with overloaded relational and stream insertionoperators.

The Money ClassThe Money class defined in money.h and money.cpp implements a cur-rency data type from a float type with overloaded relational and insertionoperators. Its constructor rounds the fractional part of the numericalvalue to even hundredths to emulate the behavior of dollars and cents.

The GUI Class, a Generic User InterfaceThe following source files are discussed:

+ gui.cpp

* gui.h

LinkedList<int> IntList; II a list of integersint i- 123;

IntList.AppendEntry(&i); 1/ append an entry to the list

U navigate the listint *ls - IntList.FirstEntryo;

while (Is !- 0) f

// ... (use the it pointed to by Is)Is - IntList.NextEntryo;

Page 170: C++ Database Development 1558283579

162 C++ Database Development

Every software development activity targets a user environment andmust, therefore, code to a particular user interface model. The exampleprograms in this chapter are no exception. They are launched from theubiquitous command-line interface, and they use a simple text-modescreen model to display information. To encapsulate the console func-tions, this book includes the GUI class, which manages menus, data anderror message displays, and keyboard input through a combination ofANSI protocols and PC-specific BIOS calls.

Do not expect to use the GUI class outside the example applicationsin this chapter. Most programmers write programs for far more sophisti-cated user interface environments. The GUI class provides support forthe example programs at the lowest common console denominator. If youuse something other than an ANSI terminal or a PC with MS-DOS orOS/2, you must modify the GUI class.

The use of this class in these examples does not in any way tiePARODY to text-mode, iostream applications. The GUI class is not apart of PARODY, which is independent of any particular user inter-face. PARODY is a class library that implements persistent objectswithout regard to the user interface of the application that uses thepersistent object classes.

The DisplayTo run the example progams without modification to the screen displaysoftware, you need an ANSI-compatible terminal or a software driverthat emulates the ANSI protocols for cursor positioning and clearing thescreen.

If you are using MS-DOS to run these programs, make sure that theCONFIG.SYS file in the root directory of the boot disk contains thisstatement:

It may be necessary to add a DOS path to the file name.

If you are using OS/2 to run these programs and the displays appearto be jibberish, enter this command at the OS/2 command line:

Page 171: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 163

ANSI ON

The KeyboardThe GUI class uses functions from MS-DOS and OS/2 C++ compilers totest for keyboard input and read the keyboard. The standard cin objectdoes not satisfactorily provide for single key input, which menu selectionsuse. The object has no way to test for the presence of a keystroke withoutactually reading it, and the sample applications require such a feature.Furthermore, there is no consensus among C++ compiler vendors for theimplementation standards of iostreams. All the compilers behave differentlyin small ways. The GUI class uses iostreams, but not for everything.Instead, it uses platform-specific, compiler-supplied function calls into theBIOS of the PC.

The gui ObjectThe gui.h and gui.cpp source files define and implement the GUI class. Theexample applications declare a global pointer to an object of that classnamed gui and use the new operator to build a GUI object. The GUI classconsists mostly of methods that manage keyboard input and screen output.The gui.h header file defines constant values to establish screen dimensionsand the values returned for certain function keys. If you use function keyvalues other than those defined in gui.h, you should add constants to thatlist. The discussion on the GetKBChar member function explains thatprocess.

The GUI class manages menus, data input, output to the screen, andkeyboard operations.

DATA INPUT

The GUI class includes several overloaded UserInput functions thatprompt the user to enter a data value and then read the value into a spec-ified object. Figure 10.4 shows the use of these functions.

The UserInput call to load a string object specifies the length of the stringto be read. The object's length is adjusted to reflect the specified length.

Page 172: C++ Database Development 1558283579

164 c++ Database Development

FIGURE 10.4 Calling the UserInput function

SCREEN OUTPUT

The G UI class includes these four member functions to perform screen output:

+ SetCursor positions the screen and keyboard cursor at a speci-fied x/y coordinate.

+ ClearScreen clears the screen.* WriteChar writes a single character at a specified x/y coordinate.* StatusLine writes a line of text at the bottom of the screen,

padding the line with spaces.

Figure 10.5 shows the use of these functions.

gui->ClearScreeno;gui->SetCursor(5.10);

cout << "Hello, Dolly";gui->WriteChar('!,17,10);gui->StatusLine("Al1 done");

clearr the screenposition cursor:x-5,y-10IIwritten at 5/10//punctuate the messageIIwrite a status line

FIGURE 10.5 Screen output

// ----- character input

char c;

Userlnput(&c, "Enter a letter");// ----- integer input

int i; // could be longUserlnput(&i, "Enter a number");

// ----- string inputstring name;

Userlnput(&name, "Enter your name", 25);

// ----- Date input

Date dt;

UserInput(&dt, "Enter your birthday");

// ----- Money input

Money wage;UserInput(&wage, "Enter your hourly wage");

Page 173: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 165

KEYBOARD INPUT

The GUI class includes three member functions for keyboard input of asingle keystroke:

* KBCharWaiting returns true if there is a keystroke waiting tobe read.

+ GetKBChar waits for a keystroke, reads it, and returns its value.* PutBack stuffs a keystroke into the input buffer so that it is

returned by the next GetKBChar call.

Figure 10.6, which assumes the presence of the gui pointer declared bythe example applications, shows the operation of the keyboard functions.

if (gui-KBCharWaiting0)c - gul->GetKBChar0;

gui ->PutBack(ESC);

FIGURE 1 0.6 Keyboard input

When the user presses a function key, the GetKBChar function returnsthe key's ASCII scan value logically OR'd with the value Ox8O. The con-stant integers in gui.h define those values for the PC's cursor arrow keysand the Escape key. If your application needs other function key values,you can add to that list. To determine the values, write a program such asthe one in Figure 10.7.

#include <iostream.h>#include "gui.h"void main()

char c;cout << "Type keys. Esc when done" << endi;while ((c = gui.GetKBCharo) !- ESC)

cout << "That key's value is:< ((int) c & 255) << endl;

FIGURE 10.7 Finding the key values

Page 174: C++ Database Development 1558283579

166 C++ Database Development

DIALOGS

The GUI class includes three simple dialogs that the applications use. TheYesNo function displays a question, reads the user's answer, and returns trueif the user answered yes, and false if the user answered no. The Error func-tion displays a specified error message along with an audible alarm andprompts the user to enter any key to continue. The AnyKey function promptsthe user to enter any key. Figure 10.8 shows the use of these three dialogs.

if (1gui->YesNo("Is that OK?"))gui ->Error("Sorry"');

el segui ->AnyKeyO;

FIGURE 10.8 User dialogs

MenusTwo menu formats are supported by the GUI framework: a full-screen menuand a one-liner menu. They are implemented by the ScreenMenu andOneLineMenu classes, which are defined in gui.h and gui.cpp. An applicationdeclares an object of the particular menu class, provides the menu details asarguments to the constructor, and calls the class's Execute member function.

FULL-SCREEN MENUSFigure 10.9 illustrates how an application invokes a full-screen menu bydeclaring and initializing a ScreenMenu object.

FIGURE 10.9 Executing o full-screen menu

Page 175: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 167

The first argument in the ScreenMenu constructor is the menu title.Subsequent arguments are pairs of menu selection labels and commandfunctions to execute when the user chooses the selection. Because theconstructor uses a C-style variable argument list, the last argument mustbe a null pointer. You must use the NULL global identifier here instead ofa constant zero. The constructor is declared with one parameter and anellipse to form a variable argument list. The compiler would not know toconvert a zero argument to a null character pointer. In an environmentwhere integers and pointers are different lengths-such as typical MS-DOS 16-bit programs-the conversion would be wrong, so use NULL tobe sure.

Note: Many C++ purists deprecate the use of variable argumentlists because of the absence of type checking. There are otherC++ techniques that I could have used to implement theScreenMenu class. One such approach is to define a menuselection class, declare arrays of menu selection objects, andpass the address of the arrays to the constructor. That conven-tion would remain within the spirit of strong type checking andwould keep the purists happy. I find, however, that the variableargument list notation is convenient and expressive for thesetypes of operations. If it offends you, then by all means changeit. The NULL global identifier is out of favor as well, for verygood reasons. C++ programmers prefer to use the constant 0instead, using the compiler's automatic type conversion of inte-ger zero to address zero where a pointer type is expected. Inthis case, the type conversion does not work for the reasonsstated above.

By using the dot operator after the constructor to call the Execute func-tion, you do not have to name the menu object. This practice is for nota-tional convenience to obviate unnecessary identifier declarations. All youcan do to a ScreenMenu object is declare it and call its Execute function.An application might declare and name all menus globally and then exe-cute them in appropriate places in the program. Either way works.

The ScreenMenu declaration in Figure 10.9 clears the screen and dis-plays the menu shown in Figure 10.10.

The user selects a menu command by typing its number. The programexecutes the function associated with the command.

When the command function returns, ScreenMenu clears the screenand displays the menu again. If the command function displays informa-tion that the user must see before returning to the menu, the command

Page 176: C++ Database Development 1558283579

168 C++ Database Development

mand function should call the GUI object's AnyKey function to promptthe user to press a key before proceeding. When the user presses the Esckey at the menu's Select> prompt, the ScreenMenu constructor is com-pleted, and the statement following the constructor executes.

FIGURE 10.10 A GUI full-screen menu

ONE-LINER MENUS

One-liner menus are different from full-screen menus in these respects:First, one-liner menus do not clear the screen. Second, rather than dis-playing in the center of the screen, the menu text is displayed whereverthe cursor happens to be when you declare the OneLineMenu object.Third, the one-liner menu does not loop while waiting for the user topress Esc or any other exit key. Instead, the menu takes one selectionfrom the user and then returns. Fourth, there is no menu title; the menutext consists of one line that describes the menu selections. Finally,instead of using digits for menu selection keystrokes, the one-liner menufigures out which keystroke matches each selection based on the firstcharacter of each selection's text. Figure 10.11 is an example of the codethat invokes a one-liner menu.

void ChangeNameo, ChangeDept();OneLineMenu("N-ame, D-epartment".

ChangeName. ChangeDept) .Executeo;

FIGURE 10.11 Invoking a one-line menu

The code in Figure 10.11 constructs a OneLineMenu object, which dis-plays the first text argument on the screen and waits for the user to press

Employees

1: Add

2: Change

3: Delete

4: Li st

5: Projects

Esc: Return

Select>

Page 177: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 169

N or D. Depending on which key the user presses, the object executesone of the two functions named as the second and third arguments.There is no need to have a NULL final argument because the constructoruses the number of discrete words in the text to determine not only thecommand keys but also the number of selections on the menu. Thismeans that each discrete selection must be represented by a label thatincludes no white space.

Navigational Access: The PayrollApplicationThe following source files are discussed:

+ payroll.cpp

* payroll.h

Needless to say, the code in payroll.h and payroll.cpp does not implementa complete payroll system. These files demonstrate the use of thePersistentObject<T> template class to add persistence to simple flat struc-tures without key data members.

The payroll.h header file declares the PayrollRcd structure, whichcontains only fixed-length data members. It contains no virtual functionsand no pointer or reference data members. You can store and retrieveobjects of this structure in and from a PARODY persistent object data-base by using the type as the argument when declaring objects of thePersistentObJect<T> template class. The program's main functiondeclares GUI and Parody objects and uses a ScreenMenu object to runthe program. The menu has selections to add records to the database andto list the records in the database. The essence of adding a record isshown in Figure 10.12.

PayrolIRcd pr;// ... put some data into prPersistentflbject<Payro1lRcd> ppr(pr);ppr.AddObjectoC;

FIGURE 10. 12 Adding a PersistentObjectkT> object

Page 178: C++ Database Development 1558283579

170 C++ Database Development

The example in Figure 10.12 represents the simplest use of PARODY. Itcontains all the code necessary to store a flat, simple object into the data-base. The object itself is an instance of the structure. The persistent objectis an instance of the PersistentObject template class with the structure asthe parameterized type.

The PersistentObject template assumes that the size of the structure isthe size of the object's record. The template further assumes that every-thing within the structure is persistent and independent of its originalmemory address, which means that the record may be read into a differ-ent instance of the structure in a different program at a different timewith no attendant problems. Had it needed to, the program could haveobtained the object's database address by calling thePersistent':Ob:ectAddress function while the object was still in scope.Figure 10.13 shows how that would have worked.

ObJAddr oa - ppr.ObjectAddress(;

FIGURE 10.13 The address of a persistent object

The ListRecords function in payroll.cpp navigates all the PayrollRcdobjects in the database in the coincidental order in which they wereadded. Figure 10.14 illustrates this process.

Persistent~bject(PayrollRcd> ppr;ppr. Fi rstObjecto;while (ppr.ObjectExistso) t

ppr.NextObject(;)I

FIGURE 10.14 Navigating PersistentObject<T> Objects

This simple payroll application represents the requirements for keylessobjects. Without keys, the application itself keeps track of where the indi-vidual objects are in the database. Many applications would use thisstrategy. Many others use a unique data member value to identify persis-tent objects. Those applications are best served by key data members,and the next example demonstrates that usage.

Page 179: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 171

Associative Access: The Game of LifeApplicationThe following source file is discussed:

Slife.cpp

The Game of Life, used here to demonstrate keyed persistent objects,dates to the early days of computer simulation. The game was inventedby British mathematician John Conroy and was published in ScientificAmerican in 1970.

The simulation is small enough to lend itself well to an illustration ofpersistent objects. The game involves relatively few lines of code, and thecondition of its simulation at any time is an object that can be stored andretrieved in an object database.

The Concept of LifeLife simulates a world of neighboring cells. Each cell, identified by its x/yaddress in the screen coordinate system, may have one of two possiblestates. The cell is either populated or unpopulated and is surrounded byeight neighboring cells, as shown in Figure 10.15.

0000W0000

FIGURE 10.1 5 A Life world

The neighborhood wraps around. Cells in the margins are neighbors withadjacent cells in the opposite margins.

Page 180: C++ Database Development 1558283579

172 C++ Database Development

The game consists of a sequence of generations. Each generationexamines each cell to see whether the cell is populated and how manyneighbors it has. A neighbor is one of the eight adjacent cells in the 3x3array of nine in which the target cell is the center cell.

A cell is born and survives or expires depending on the population ofits neighborhood. Its survival depends on having enough neighbors forcompany and support and not too many with whom to share resources.With each generation, a cell is born-becomes populated-if there areenough adjacent neighbors to spawn it. The cell dies-becomes depopu-lated-if there are either too few neighbors to support it or too manyneighbors with which it must share resources. More specifically, if anunpopulated cell has three-no more, no less-populated neighbors, thecell becomes populated in the next generation. Conversely, if a populatedcell has fewer than two or more than three neighbors, its populationexpires in the next generation.

The implementation of Life in this book uses screen character posi-tions to represent cells, and the world of cells is limited to the number ofscreen character positions.

An Evolution of GenerationsThe Game of Life simulates the evolution of generations. To play thegame, you create the world by specifying which cells are initially populat-ed. Then you run the evolution and observe how each generation modi-fies the pattern of populated cells. You can run the evolution continuous-ly or by stepping through it one generation at a time. The world oftentakes on interesting symmetrical patterns as the generations pass. Somepatterns result in a totally depopulated world after a few generations.Some patterns result in a stable, populated world. Some patterns endless-ly repeat cycles of births and deaths. A culture of Life players blossomedat MIT and elsewhere in the 1970s, and they often published and sharedinteresting starting patterns.

The Life ProgramLife.cpp is the source file that implements the game. It uses persistentobjects of type Life to save and retrieve patterns. You can design a pat-

Page 181: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 1 73

tern and save it as a persistent object, or you can stop an evolution mid-way and save the current pattern. To save an object, you must provide aname. The program adjusts the size of the name to a fixed-length stringobject and uses the name as the object's key. Later, you can retrieve pat-terns by their names and play the game by beginning with the retrievedpattern.

The persistent Life object is built from the state of the in-memoryLife object when the program adds it to the database. The object in thedatabase consists of the object's name, a count of populated cells, and alist of the x/y coordinates of the populated cells. The persistent objectis, therefore, a variable-length data structure that does not resemble thein-memory object at all. The Life:: Write and Life::Read functions definethe database object format.

This is an important example. Unlike the records of traditional data-bases, a persistent object's representation does not necessarily resemblethe object's in-memory representation. Instead, the persistent representa-tion contains sufficient data to reconstruct the in-memory representation.The details of that representation and reconstruction are provided by theclass's Read and Write member functions.

Rules for SurvivalThe in-memory object includes a table of rules for cell survival. As pub-lished, the rules reflect those of the traditional Game of Life as Conroydesigned it. You can modify this table to achieve different results duringthe evolution of a Life session. The Life::Rules array consists oftrue/false state indicators. The outer dimension contains two innerarrays: the first applies to a cell that is unpopulated; the second appliesto a cell that is populated. The inner array has one entry for each possi-ble count of neighbors. For example, if the cell has four neighbors, thenthe fifth entry specifies whether that cell is to be populated in the nextgeneration.

Running the ProgramWhen you run the program, it displays the menu shown in Figure 10.16.

Page 182: C++ Database Development 1558283579

174 C++ Database Development

FIGURE 10.16 Running the Game of Life

Select 1 to build a new world. The program displays a mostly blankscreen with a status line at the bottom. Use the cursor arrow keys tomove the cursor around, and press the space bar or the Enter key to tog-gle the population of a cell. A populated cell displays as a happy facecharacter on a PC. You can change the display character by changing thevalue of the Pop constant in life.cpp. Figure 10.17 shows the screen witha typical pattern.

FIGURE 10.17 A Game of Life pattem

When the screen has the population configuration you want, pressEsc to return to the menu. Table 10.1 lists the Game of Life menuactions.

Page 183: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 175

TABLE 10.1 Game of Life menu selections

SELECTION ACTION

1 Builds a new world pattern of populated and depopulated cells.

2 Returns to the pattern screen and allows you to make further modifications to the pattern.3 Deletes from the database the Life object that you loaded with selection 8.4 Displays the names of all the Life objects in the database.

5 Displays the current pattern.6 Runs the evolution of generations nonstop, displaying the patterns while you watch. Interrupt

the process by pressing Esc.7 Displays the current generation and allows you to step through subsequent generations a step

at a time by pressing the S key.8 Retrieves a Life object from the PARODY database. The program prompts you to enter the pat-

tern's name.9 Saves the current pattern in its current generation as a persistent object in the PARODY database

named LIFE.DAT. The program prompts you to provide a name for the pattern in the database.

Almost Relational: The PersonnelApplicationThe following source files are discussed:

* assign.cpp* assign.h

* dept.cpp+ dept.h

+ employee.cpp

* employee.h+ index.cpp

* manager.cpp

*manager.h

*personel.cpp

Page 184: C++ Database Development 1558283579

1 76 C++ Database Development

* personel.h

* project.cpp

* project.h+ projview.cpp

This section describes the Personnel system, a PARODY application thatmaintains a database of departments, employees, projects, and employee-to-project assignments. This system uses PARODY's almost-relationalproperties to maintain indexed objects and interclass relationships. Figure10.18 illustrates the relational and hierarchical architectures of thePersonnel application database.

MANAGER:

FIGURE 10.18 Personnel application object architectures

The Personnel application stores records of departments, employees, pro-jects, and employee-to-project assignments. An employee may work foronly one department. Departments and projects may have one managereach. The managers are employees. Employees are assigned to projectsand log labor hours against those projects. An employee may work onseveral projects. A project may have several employees assigned to it.

Page 185: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 1 77

We will first discuss the Personnel application's persistent classes andthen we will address how the application uses them.

The Department ClassDept.h and dept.cpp define and implement the Department class, whichis typical of a simple persistent object class. In addition to its deptno pri-mary key, it includes data members that define the department name and,as a secondary key, the manager object, which is the employee number ofthe department manager. The EmployeeNumber type is defined inemployee.h, which you will see later, so the dept.h file includes it prior tothe Department class definition.

There are the usual Read and Write member functions to support per-sistence and some member functions to read and change the data values.

The Department class encapsulates certain aspects of the user inter-face required to retrieve, display, and modify the object. This approach isconsistent in the design of the other classes in the system, and it consistsof member functions to display the object, read its data member valuesfrom the keyboard, and prompt for and retrieve or build instances of theobject. The related member functions are Input, InputName,InputManager, TryChange, SelectChange, Get, GetExisting, Header, andan overloaded ostream insertion operator.

The Employee ClassEmployee.h and employee.cpp define and implement the Employee class,with its emplno primary key. The Employee class has a secondary key aswell, the deptno object, which specifies the department to which theemployee is assigned. The only other data member is the employee'sname. The remaining class definitions consist of member functions thataccess and modify data values and that encapsulate the user interface inthe same way that the Department class does.

The Project ClassProject.h and project.cpp define and implement the Project class, which issimilar in construction to the Department class. It includes a promno pri-mary key and a manager secondary key to identify the employee numberof the project manager. The other two data members are the project name

Page 186: C++ Database Development 1558283579

178 C++ Database Development

and an integer that represents the hours expended against the project.The member functions include the usual data member access and modifi-cation functions as well as one that allows a using program to add to thehours expended. The user interface isý encapsulated just as in theDepartment and Employee classes.

The Assignment ClassAssign.h and assign.cpp implement the Assignment class, an example of arelational connector class that forms the many-to-many relationshipbetween objects of the Employee class and objects of the Project class.The assignment object is the concatenated key that makes the connection.It is built from the CatKey<T1,T2> class template and it contains twomember objects built from EmployeeNumber and DepartmentNumberobjects. In addition to forming the concatenated primary key, the twoobjects are secondary keys. The rest of the class definition is similar tothe other classes with member functions that access and change data val-ues and with an encapsulated user interface.

The Manager ClassManager.h and manager.cpp implement the Manager class, whichdemonstrates deriving a class from a persistent class. In this case, theManager class is derived from the Employee class. All that it adds to theclass is a bonus data member of type Money.

To use persistent classes in a class hierarchy, the base class must coop-erate with the inheritance. Its Read and Write member functions must bevirtual, and it must be prevented from calling LoadObject from its con-structor and SaveObject from its destructor. Except for those differences,a base persistent class is identical to any other persistent class.

The Employee class is not an abstract base class. You can haveEmployee objects that are of type Employee rather than a derived type.Therefore, the class needs to know, during construction and destruction,whether it is operating as a base class. Normally, C++ classes have noway of knowing that. Therefore, we must contrive something.

Observe the Employee constructor and destructor in employee.cpp.They call LoadObject and SaveObject only if the employee number isnot -1. We have set that convention for classes that will derive from

Page 187: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 179

Employee. You can use other methods of a similar nature in yourdesigns. The idea is to have the derived constructor and destructor notifythe base constructor and destructor that the derivation exists. Inasmuchas -1 is not a valid employee number (according to a rule that we justmade up to fit the occasion), we can use that value as a signal to thebase class.

The Manager constructor in manager.cpp initializes the Employeeconstructor with the value -1 in the parameter initialization list. Thatvalue tells the Employee constructor not to call LoadObject. Then,the Manager constructor calls Employee::SetEmplNo to set theemployee number to a valid value. After that, the constructor callsLoadObject.

This sequence is important because while the Employee constructor isrunning, the Manager object does not yet exist. The Manager object'sRead function would not be called, and PARODY would associate theindexes with the Employee class rather than the Manager class.

The Manager destructor calls SaveObject and then sets the employeenumber to -1 so that the Employee destructor does not call SaveObject.

Observe that the relationships established between Employee andother classes are not automatically inherited by the Manager class. It is aclass unto itself-albeit a derived one-and if relationships are to bemaintained, you must establish them separately from those of the baseclass.

The Personnel ProgramPersonel.h and personel.cpp implement the Personnel application. Theheader file declares the typedefs for the application's key data elements,includes the header files that define the persisent classes, and declares theexternal pointer to the GUI object.

Personel.cpp implements the application with a series of GUI menusthat allow you to enter data into the database and display the databasecontents. The first menu has selections for employees, departments, pro-jects, and assignments. Each of these selections allows you to add,change, list, or delete objects of the chosen class as well as some uniqueprocesses for each class. The add, change, list, and delete processes aresimilar in all the classes, so this discussion covers those functions in gen-eral and then discusses the unique processes for the classes that illustrate

Page 188: C++ Database Development 1558283579

180 C++ Database Development

different ways that an application uses PARODY where objects arerelated.

ADDING AN OBJECT.

This discussion relates to the AddEmployee, AddDepartment,AddProject, and AddAssignment functions in personel.cpp.

The add functions for the classes first call the static Get function forthe class, which prompts the user to enter a value for the primary key-employee number, for example-and then instantiates an object from thekey value. The Get functions use the new operator to instantiate theobjects on the heap and return their pointers, which the caller mustdestroy with the delete operator or Persistent::Destroy member function.If the object's ObjectExists function returns true, the add function dis-plays the object along with an error message that the user is trying to addan object that already exists.

CHANGING AN OBJECT

This discussion relates to the ChangeEmployee, ChangeDepartment,ChangeProject, and ChangeAssignment functions in personel.cpp.

The change function calls the object's GetExisting function, whichprompts the user for a primary key and instantiates an object from thekey value. If the object does not exist in the database, GetExisting dis-plays an error message, destroys the empty object, and returns a nullpointer. Otherwise, it displays the object and returns the address of theobject, which the caller must later destroy by using the delete operator orPersistent::Destroy member function. The change function calls theobject's SelectChange function, which prompts for one of the variousdata fields in the object and accepts the user's input to change the data. IfPARODY rejects the change due to an integrity violation, SelectChangedisplays an error message.

LISTING AN OBJECT

This discussion relates to the ListEmployee, ListDepartment, ListProject,and ListAssignment functions in personel.cpp.

The list function calls FirstObject to retrieve the first object for theclass. Then, as long as the object's ObjectExists function returns true, thelist function displays the object on cout and retrieves the next object in itsprimary key sequence by calling its NextObject function.

Page 189: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 181

DELETING AN OBJECT

This discussion relates to the DeleteEmployee, DeleteDepartment,DeleteProject, and DeleteAssignment functions in personel.cpp.

The delete function uses GetExisting the same way that the changefunction does. If GetExisting returns a valid object pointer, the deletefunction asks the user to verify the deletion in a GUI::YesNo dialog.Then the delete function calls DeleteObject to delete the object from thedatabase, displaying an error message if PARODY rejects the deletiondue to an integrity violation.

LISTING AN EMPLOYEE'S PROJECTS AND A PROJECT'S EMPLOYEES

The ListEmployeeProjects and ListProjectEmployees functions illustratehow a program navigates a connector class by using a secondary keycomponent of its concatenated primary key. The program callsGetExisting to get an instance of the project or employee, depending onwhich secondary key you are searching. Then it instantiates an emptyAssignments object, adding the partial concatenated key value to theobject. Next, it calls FindObject, specifying the secondary part of theconcatenated key to find the first instance on the database of anAssignment object with the specified project or employee number. Then,as long as the object's ObjectExists function returns true and its partialkey value is equal to the original value entered by the user, the programinstantiates an object of the other type in the concatenated key, displaysthat object, and calls NextObject for the connector class.

ADDING A MANAGER AND LISTING MANAGERS

These processes resemble those of the same operations for employees.The program does not attempt to validate that managers are employeesor that department and project managers are manager objects. TheManager class is added to illustrate persistent class inheritance only.

The Project View ProgramRecall from Chapter 8 that PARODY throws an exception when youtry to instantiate more than one copy of the same persistent object. Theprojview.cpp program demonstrates how you can use that feature toadvantage, and Figure 10.19 illustrates that logic.

Page 190: C++ Database Development 1558283579

182 C++ Database Development

FIGURE 10.19 Multiple-copy objects

The program displays two Project objects from the database so thatthe user can compare them. It calls the Project::Get function twice toprompt the user for the project numbers and to instantiate the selectedProject objects. If the user enters two valid and different unique projectnumbers, the program displays those two projects and terminates. If,however, the user enters the same project number twice, theProject::Get function tries to instantiate the object a second time, andPARODY throws the Persistent* exception. If a program ignores thisexception, the program aborts. Projview.cpp does not ignore the excep-tion; it catches the exception, displays a message to that effect, andcopies the thrown pointer into the second pointer. The second pointer isnull prior to the copy because the construction failed as a result of theexception. Now, when the program uses the two pointers, they point tothe same object. PARODY maintains a count of the number of times apersistent object has been instantiated. The Persistent::Destroy functiondecrements this count and does not actually delete the object until thecount goes to zero.

Page 191: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 183

Rebuilding the IndexIndex.cpp is a utility program that rebuilds the indexes for the Personnelapplication database. It follows the logic described in Chapter 9 for rebuild-ing indexes and is aware of all five classes that the Personnel system uses.

Persistent References: The Family TreeApplicationThe following source files are discussed:

+ family.cpp+ family.h+ famtree.cpp

This application maintains a family tree database, that contains objects offamily members. Each object records the family member's name, sex,dates of birth and death, and parents. The example demonstrates the useof the Reference<T> template class in recording persistent references torelated objects in a class. In this case, the parents of a family member arerepresented by persistent references to their persistent family memberobjects in the database.

Although this example application is not a comprehensive family treemaintenance system, its data members comprise the minimum necessarydata elements needed for a family tree. Most such systems add a memo fieldto record biographical notes about the family member. They also usuallyinclude a marriage object that records the spouses and dates of marriage anddivorce for each such union. A database with these few data members cansupport virtually all functional requirements for a family tree system.

The FamilyMember ClassThe family.h header file and the family.cpp source file define theFamilyMember class, the one persistent object class in the family tree data-

Page 192: C++ Database Development 1558283579

1 84 C++ Database Development

base. The name field is a string object and is the key data member thatidentifies the object. The sex field is an enum that can contain the valuesunknown, male, and female. The dates of birth and death are objects of theDate class used earlier in this chapter. The mother and father data membersare objects of the template class Reference<FamilyMember>. The databasestores references to other objects of the same class within these members.

Because the FamilyMember class can be referenced by aReference<T> object, it has a conversion constructor that constructsthe object from its ObjAddr identity. The application does not usual-ly use this constructor, but it is required for the Reference<T> tem-plate class to use to construct a referenced object when a referencingobject is constructed. In this example, the referencing object and thereferenced object are of the same class. This circumstance is notalways the case.

The FamilyMember class includes member functions that set andretrieve its data members. Observe that the member functions to set theparent objects simply assign FamilyMember objects to theReference<FamilyMember> data members. This action is all that is need-ed to establish a persistent reference to another persistent object.

Maintaining a Family TreeThe famtree.cpp source code file implements the family tree application.Remember that this application maintains the database only. It does notperform relationship retrievals, build ancestor and descendant diagrams,or support any of the other complex operations that a full family tree sys-tem supports. The data values are there, but to go beyond the simpleoperations of this example you must add functionality to the algorithmsthat they support.

The program displays a menu with three selections: add a record, listthe records, and view a record. The primary key to this database file isthe family member's name. To complete the design, you would have toaugment this key with a suffix that permitted several family members tohave the same name, which is a common occurrence in most families.

Adding a record consists of providing a name, the sex, and the datesof birth and death. Enter three space-separated zeros for the date ofdeath when the family member is still alive.

Viewing a member prompts you for the member's name. TheGetListedMember function lets you supply a portion of a name and lists

Page 193: C++ Database Development 1558283579

Chapter 10: PARODY in Applications 185

those that are close to the specification. If a name matches what youenter, that name is used; otherwise, the program displays the list and letsyou select from the list.

After displaying the specified family member's record, the programuses a one-liner menu that allows you to change or delete the record. Ifyou choose to change it, another one-liner menu lets you choose tochange the name, sex, dates, or parents. Of interest here is how the pro-gram changes the parents.

When you have specified a name for a parent and when the programhas determined that the selected parent is of the correct sex, the programestablishes a persistent reference from the offspring to the parent'sFamilyMember object by assigning that object to the offspring object'sReference<FamilyMember> data member.

SummaryThis chapter provides example applications and utility programs for thePARODY developer. Appendix A shows you how to build the softwareby using one of the MS-DOS or OS/2 C++ compilers. Appendix B is aprogrammer's reference guide to PARODY. The complete source code forPARODY and the example applications is in Appendix C.

Page 194: C++ Database Development 1558283579

Appendix A

Building the SoftwareThis appendix describes how to build the programs in this book by usingone of the supported compilers. PARODY compiles with Borland C++4.0 for DOS and Borland C++ 1.5 for OS/2. Those are the only PC com-pilers that support all the new ANSI features that PARODY uses.Eventually, more compilers will support the new features, and I will addsupport for those compilers. The features themselves will probablychange as the committee proceeds with its work. There are new featuresunder consideration-such as namespaces-that are not yet implement-ed. As they become available, PARODY will change to incorporate theones that benefit the software.

The procedures in this appendix use the command-line environmentsof the compiler products. Both compilers have integrated developmentenvironments, and you might want to install PARODY and the exampleapplications into one of them. However, as published here, PARODYuses the command-line MAKE, compiler, librarian, and linker programsthat come with the compiler products.

There are two files, other than the source code itself, that are neededto build the software. They are makefile, which is the command file readby the MAKE utility program, and parody.bld, the command file that thelibrarian uses to build the PARODY object library. The listings for thesefiles are at the end of this appendix.

187

Page 195: C++ Database Development 1558283579

188 C++ Database Development

General InstructionsThese instructions apply to both compiler environments. Follow themfirst and then proceed to the instructions related to your compiler. Ifthere are additional compilers supported after the publication of thisbook or if newer versions of the compilers necessitate changes to theseprocedures, the companion diskette will describe the changes in a filenamed README.DOC.

Install Your CompilerTo use either of the compilers, first install it according to the vendor'sinstructions. Make sure that you have set the DOS or OS/2 path toinclude the subdirectory where you installed the compiler's executablefiles. Most compiler installations offer to do this for you. The symptomsthat occur when your compiler is not properly installed usually point tomissing configuration file items. The compilers use these controls to tellthem where to find the system's header and library files.

Many of the letters I have received from readers about their prob-lems building the code from the first edition of C++ DatabaseDevelopment were, in fact, problems with their installation of thecompiler. Before you assume that a problem originates in the PARODYcode or these instructions, verify that your compiler will compilesomething else. Build the ubiquitous HELLO.C program that most Clanguage texts begin with and make sure that you can compile, link,and run that program. Use the command-line compiler to build the testprogram. Often a compiler is configured correctly for the integrateddevelopment environment but is not configured to work from the com-mand line.

Install the PARODY Source CodeMake a subdirectory to contain the source code and copy the associatedmakefile and parody.bld files into that subdirectory. Copy the sourcecode from Appendix C into the same subdirectory.

Log into the subdirectory where you copied the PARODY sourcecode.

Page 196: C++ Database Development 1558283579

Appendix A: Building the Software 189

Modifying MakefileIf you wish to build programs for a memory model other than the largemodel, change the MODEL macro in makefile from '1' to 's' for the smallmodel, 'c' for the compact model, or 'in' for the medium model. Notethat an OS/2 installation is not concerned with memory models, and youmust comment out the definition of the MODEL macro in makefile.

Building the Executable ProgramsEnter the following command at the DOS or OS/2 command line to runthe Borland C++ MAKE program and build the software.

ma ke

The MAKE program builds these files:

+ parody.lib-The PARODY object library with which all appli-cations link.

* index.exe-An example of a program to rebuild a database'sindexes.

* personel.exe-The Personnel application.* projview.exe-A program that illustrates instantiating multi-

ple copies of objects.

* payroll.exe-The Payroll application.* life.exe-Conway's Game of Life.

* famtree.exe-The Family Tree application.

Testing the SoftwareObserve the screen while the compiler's MAKE utility builds the soft-ware. There should be no error messages. There will probably be nowarning messages either, but compiler warnings are not consistent, andfuture versions of compilers might issue warnings where current onesdo not.

Page 197: C++ Database Development 1558283579

190 c++ Database Development

If everything went smoothly, the files listed above will be in the subdirec-tory where you installed the source code.

ANSI.SYSBefore you run the applications, make sure that the DOS ansi.sys devicedriver is installed in config.sys. For OS/2, make sure that the ANSI ONcommand is in effect. If it is not, you will see a display similar to the oneshown in Figure A.1 when you run life.exe.[D: \PARODY2> Life

*[2J-E7;21HThe Game of Life-E9;23H1: Ne•÷-E18;23H2: Change-E11;23H3: De lete.12;23H4: List-E113;23HS: Displa&U.-E14;23H6: Run÷E15;23H?: Step÷[ 16;Z3H8: Load.t1T7;23H9:Save-E18 ;21HEsc: Return.128 ;21HSe |ect>

FIGURE A. 1 The Game of Life without ANSI protocols

The display just shown contains the ANSI command protocols that clearthe screen and position the cursor embedded in the menu text. If the DOSansi.sys device driver is not installed or if the OS/2 ANSI ON commandhas not been issued, the operating system displays those commands onthe screen as if they were common text.

Run the Example Application ProgramsRun the life.exe program. Exit it immediately. There should now be twoadditional files: life.dat and life.ndx. They are the PARODY database forthe example Life application. Each will be only four bytes long, becauseyou have not added any objects.

Run the personel.exe program. Exit it immediately. There shouldnow be two additional files: personel.dat and personel.ndx. They are thePARODY database for the example Personnel application. Each will beonly four bytes long, because you have not added any objects.

Run the personel.exe program and add, change, list, and deleteemployee, department, project, and assignment objects by using the menucommands. Exit and rerun the application to see that the objects are, infact, persistent.

Page 198: C++ Database Development 1558283579

Appendix A: Building the Software 191

Run the life.exe program. Build and save worlds of populated cells byusing the menu commands. Exit and rerun the application to load thoseobjects.

Test the payroll.exe, projview.exe, and famtree.exe programs by run-ning them, adding objects where appropriate, and viewing those objectsto see that the database is updated correctly.

Rebuild the IndexesRun the index.exe program. It displays each of the objects in thePersonnel database as it finds them and rebuilds the indexes. You can ver-ify that index.exe works by deleting or renaming the personel.ndx filebefore you run index.exe. After the program is done, the personel.ndx filewill be reestablished, which you can verify by running personel.exe againand retrieving objects.

ListingsFollowing are listings of the files that build the PARODY library and theexample applications.

Makefile

continued

# makefile - Borland C++ 4.0 for DOS and 1.5 for OS/2# =. . . ..- -.-.-

# Comment out this macro to remove debugging

#DEB=UG- -v -vi =

# Comment out this macro for OS/2#•m• === - ------ ==,,

MODEL- -ml

Page 199: C++ Database Development 1558283579

192 c++ Database Development

continued

CI4PL- $(DEBUG) $(MODEL).cpp.obj:

bcc $(CMPL) -c f$* Jall : ndex.exe \

personel .exe\projview.exe\payroll.exe\life~exefamtree.exe\

echo all done# -- - - -- -# define the Personnel system~ class library# - -- - - -- - - -APOBJ-employee.obj dept.obj project.obj assign.obj rnanager.objPLINK-$&.obj $tAPOBJ)index.exe :index.obj $(APGBJ) parody.lib

bcc $(Ct4PL) $(PLINK) parody.libpersonol.exe :personel.obj $(APOBJ) parody.lib

bcc -M $(CMPL) $(PLINK) parody.libprojview.exe :projview.obj $(APOBJ) parody.lib

bcc $(CMPL) $(PLINK) parody~liIblife.exe :life.obj parody.lib

bcc 41 $(CMPL) life.ob.j parody.libpayroll.exe :payroll.obj parody.lib

bcc $(CMPL) payvroll~obj parody.libfamtree.exe :famtrea.obj faniily.ofrj parody.lib

bcc $(Ct4PL) famtree.obj family.obj parody.1ib

# Build the PARODY.LIB class library# - =-----parody.lib :parody.,obj\

btree~obj\key .bjb \tnode~obj \node.objgui,.obj \money.obj \date. obj

tlib parody @parody.bld

Page 200: C++ Database Development 1558283579

Appendix A: Building the Software 193

Parody.bid-+parody.obj &

-+btree.obj &

-+key.obj &

-+tnode.obj &

-+node. obj &

-+gui &

-+money.obj &

-+date. obj

Page 201: C++ Database Development 1558283579

Appendix B

PARODY Reference GuideThis appendix is the programmer's reference guide to PARODY. Itdescribes:

* Constants and typedefs that alter PARODY's behavior.* Class interface member functions.* Exceptions.+ PARODY data structures.

Modifying PARODYYou can change the way PARODY works by altering the values assignedto some constants and by changing some typedef declarations.

ConstantsTable B.1 shows the global constants that you can change to make com-pile-time modifications to the way PARODY operates.

195

Page 202: C++ Database Development 1558283579

196 c++ Database Development

TABLE B. I Global constants

CONSTANT DESCRIPTION DEFAULT HEADER FILE

SCREENHEIGHT Screen height 25 gui.hSCREENWIDTH Screen width 80 guLhMAXCOMMANDS Maximum menu commands 9 gui.hnodelength Length of a PARODY disk node 128 parody.h

The first three constants affect the GUI class behavior. As such they donot affect PARODY. You can change them if you plan to use the GUIclass in your applications. One possible change is to increase theSCREENHEIGHT constant to 43 or 50 and use the DOS or OS/2 com-mand-line MODE command to set the video mode to 43 or 50 lines. Trythat with the Game of Life example application to have larger world pat-terns with which to experiment.

typedefsNode.h declares NodeNbr, a typedef that you can change to affect therange and size of files. The type is a scalar representing a serial nodenumber. The internal object identity of a persistent object is aNodeNbr value relative to the value one. Zero represents a null value.The type width determines the maximum number of Nodes that youcan have in a database. As published, NodeNbr is a short integer. In atypical PC implementation, this means that a database may have65,536 physical nodes. If no object exceeds one node in length (asdefined by the nodelength constant described above), there can be asmany as 65,536 objects in the database. To increase this limit, changethe typedef to int (on 32-bit compilers) or long and recompile thesoftware before you build the database. Because the index anddatafiles contain NodeNbr objects, changing NodeNbr's widthchanges not only the ranges of the files but also their sizes. As aresult, you cannot change NodeNbr and subsequently use the soft-ware with a database built before the change. You must rebuild thedatabase from scratch.

Date.h declares DtEl, an integer typedef that stores the month,day, and year components of a calendar date. As used in the exam-ples, the type is an unsigned char. If you need to record years withvalues greater than 255, you must change this typedef or modify theDate class.

Page 203: C++ Database Development 1558283579

Appendix B: PARODY Reference Guide 197

(lass Library ReferenceThis section describes the application program interface (API) to PARODY.It identifies each of the API classes, the header file where the class isdefined, and the member functions that using programs call.

boo! (boh lh)

class bool;

A Boolean data type that may have one of twoenumerated values: true or false. You can assign aninteger value to a bool object, which stores thevalue as true or false rather than as the integer'snumerical value.

CatKey (key.h)

template <class T1,class T2> class CatKey;

A template that implements concatenated keys fora Persistent derived class. The key consists of twodata types that, if not intrinsic C++ types, mustoverload operator==, operator=, and operator>.The types must include conversion constructorsthat accept a constant zero argument to build nullvalues.

Constructor CatKey(const T1& keyl, const T2& key2)

Constructs the concatenated key with two datatypes.

Key] Key<T1>& Key1()

Returns a reference to the first (leftmost) half ofthe concatenated key as an object of typeKey<T1 >.

Page 204: C++ Database Development 1558283579

198 C++ Database Development

KeyValuel T1& KeyValuel()

Returns a reference to the object parameterized asthe first (leftmost) half of the concatenated key.

const T1& KeyValuel() const

Returns a const reference to the object parameter-ized as the first (leftmost) half of the concatenatedkey.

SetKeyValuel void SetKeyValuel(const T1& keyl)

Sets the value of the argument into the first(leftmost) half of the concatenated key.

Key2 Key<T2>& Key2()

Returns a reference to the second (rightmost) halfof the concatenated key as an object of typeKey<T2>.

KeyValue2 T2& KeyValue2()

Returns a reference to the object parameterized asthe second (rightmost) half of the concatenated key.

const T2& KeyValue2() const

Returns a const reference to the object parameter-ized as the second (rightmost) half of the concate-nated key.

SetKeyValue2 void SetKeyValue2(const T2& key2)

Sets the value of the argument into the second(rightmost) half of the concatenated key.

operator= PdyKey& operator=(const PdyKey& key)

operator== int operator==(const PdyKey& key) const

operator> int operator>(const PdyKey& key) const

Overloaded operators for assignment and compar-ison between CatKey objects.

Page 205: C++ Database Development 1558283579

Appendix B: PARODY Reference Guide 199

Date (date.h)

class Date:

Implements a calendar date with month, day, andyear values.

Constructor Date(DtE] m=O,DtE] d=O,DtEl y=O)

Month DtEI Month() const

Returns the month as a DtEl object.

Day DtEl Day() const

Returns the day as a DtEI object.

Year DtEI Year() const

Returns the year as a DtEl object.

SetMonth void SetMonth(DtEl m)

Sets the month value.

SetDay void SetDay(DtEl d)

Sets the day value.

SetYear void SetYear(DtEl y)

Sets the year value.

operator== int operator==(const Date& dt) const

operator< int operator<(const Date& dt) const

operator!= int operator!=(const Date& dt) const

operator> int operator>(const Date& dt) const

operator<= int operator<=(const Date& dt) const

Page 206: C++ Database Development 1558283579

200 C++ Database Development

operator>= int operator>=(const Date& dt) const

Overloaded operators for comparison betweenDate objects.

GUI (gui.h)

Constructor

AnyKey

ClearScreen

Error

GetKBChar

KBCharWaiting

PutBack

SetCursor

class GUI;

The GUI class implements the generic user inter-face with the standard cin and cout objects.

GUI()

void AnyKeyo) const

Displays an "Any key..." message and waits forthe user to type a key.

void ClearScreen() const

Clears the screen.

void Error(const char *message) const

Displays the message string, sounds the console'saudible alarm, and calls the AnyKey function.

unsigned char GetKBChar() const

Reads the next key from the keyboard. If no key iswaiting, waits for the next keystroke.

bool KBCharWaiting() const

Tests for keyboard input. Returns true if the userhas pressed a key; otherwise, returns false.

void PutBack(char c)

Sets the character argument c as the next valuereturned from GetKBChar.

void SetCursor(short int x, short int y) const

Positions the cursor at the screen character coordi-nates specified by x and y.

Page 207: C++ Database Development 1558283579

Appendix B: PARODY Reference Guide 201

StatusLine void StatusLine(const string& s) const

Displays a status line at the bottom of the screen.

UserInput void UserInput(string *s, const char *prompt, short len)

void Userlnput(char *c, const char *prompt)

void UserInput(short int *i, const char *prompt)

void Userlnput(long int *i, const char *prompt)

void Userlnput(Date *dt, const char *prompt)void Userlnput(Money *m, const char *prompt)

These functions display the prompt string and read theuser's input into the variable pointed to by the firstargument. The string version of the function uses thethird argument to specify the length of the input stringdata.

YesNo bool YesNo(const char *question)

Displays the question string and prompts the userto enter "Y" or "N" (case-insensitive). The func-tion returns true if the user enters "Y" and false ifthe user enters "N".

WriteChar void WriteChar(char c, short x, short y) const

Writes a single character on the screen at the x/ycoordinate.

Key (key.h)template <class T> class Key;

A template that implements keys into a Persistentderived class. The key consists of one data typethat, if not an intrinsic C++ type, must overloadoperator==, operator=, and operator>. The typemust include a conversion constructor that acceptsa constant zero argument to build a null value.

Constructor CatKey(const T& key)

Constructs the concatenated key with one datatype.

Page 208: C++ Database Development 1558283579

202 c++ Database Development

KeyValue T& KeyValueo)

Returns a reference to the object parameterized asthe key.

const T& KeyValueo) const

Returns a const reference to the object parameter-ized as the key.

SetKeyValue void SetKeyValue(const T& key)

Sets the value of the argument into the key.

operator= PdyKey& operator=(const PdyKey& key)

operator== int operator==(const PdyKey& key) const

operator> int operator>(const PdyKey& key) const

Overloaded operators for assignment and compar-ison between Key objects.

LinkedList (linklist.h)

template <class D> class LinkedList;

A template that implements a doubly linked listdata structure. A program constructs the list andadds entries that consist of pointers to objects thatthe program declares. The list does not makecopies of the entries, so the user's copies mustremain in scope as long as the entries are in thelist.

Constructor LinkedList(

Constructs a linked list of pointers to objects of aspecified (parameterized) type.

AppendEntry void AppendEntry(T *entry)

Appends an entry to the linked list.

Page 209: C++ Database Development 1558283579

Appendix B: PARODY Reference Guide 203

InsertEntry void InsertEntry(T *entry, T *curr)

Inserts an entry in the list ahead of a specifiedexisting entry. If curr is null, InsertEntry worksjust like AppendEntry.

RemoveEntry void RemoveEntry(T *entry)

Removes a specified entry from the linked list.

FindEntry T *FindEntry(int pos)

Returns the address of the entry in the positionspecified by the pos argument. Zero is the first entry.

short int FindEntry(T *entry)

Returns the position of the entry pointed to by theargument. Zero is the first entry.

FirstEntry T *FirstEntry()

Returns the address of the first entry in the list.

LastEntry T *LastEntry()

Returns the address of the last entry in the list.

NextEntry T *NextEntry()

Returns the address of the next entry in the list fol-lowing the most recent access of the list. If noaccess has occurred or if the last access retrievedthe last entry, returns the address of the first entryin the list.

T *NextEntry(T *entry)

Returns the address of the entry following theentry specified by the argument.

PrevEntry T *PrevEntry()

Returns the address of the entry in the list aheadof the most recent access of the list. If no accesshas occurred or if the last access retrieved the firstentry, returns the address of the last entry in the list.

Page 210: C++ Database Development 1558283579

204 c++ Database Development

T *PrevEntry(T *entry)

Returns the address of the entry ahead of the entryspecified by the argument.

ClearList void ClearList()

Clears all entries from the list. The objects them-selves are not deleted. They are the property of theusing program. Only the list entries that point tothe objects are deleted.

Money (money.h)

class Money;

The Money class implements a numeric class con-sisting of dollars and cents.

Constructor Money(float v=O, unsigned char w = 6)

Constructs a Money object with the v argumentvalue rounded to the nearest cent. The w argumentspecifies the width that the object assumes whendisplayed.

Value float& Value()

Returns the value of the Money object.

operator< int operator<(const Money& m) const

operator== int operator==(const Money& m) const

operator!= int operator!=(const Money& m) const

operator> int operator>(const Money& m) const

int operator<=(const Money& m) constoperator<=

Page 211: C++ Database Development 1558283579

Appendix B: PARODY Reference Guide 205

operator>= int operator>=(const Money& m) const

Overloaded operators for comparison betweenMoney objects.

ObjAddr (parody.h)

struct ObjAddr;

An integer type that specifies a persistent object'saddress.

Constructor ObjAddr(NodeNbr nd = 0)

Constructs an object address from a NodeNbrobject.

Conversion operator NodeNbr() const

Converts an ObjAddr object to a NodeNbr object.

OneLineMenu (gui.h)

class OneLineMenu;

Implements a one-line screen menu with selectionsdetermined from the first letter of each word in theone-line text string.

OneLineMenu(const char *menu ..

Constructs a OneLineMenu object consisting ofthe menu text and a variable list of function point-er arguments. The function pointers point to thefunctions to be called when the user chooses thecorresponding selection from the menu.

void Executeo)

Displays the menu text and waits for the user topress a key. If the user presses a character corre-

Constructor

Execute

Page 212: C++ Database Development 1558283579

206 C++ Database Development

sponding to the first letter or number of a word inthe menu text, Execute calls the function from theconstructor's variable argument list that corre-sponds to the selection. When the called functionreturns, the Execute function returns. If the userpresses Esc, the Execute function returns immedi-ately. If the user presses any other key, the functionsounds the audible alarm and waits for anotherkeypress.

Parody (parody.h)

class Parody;

The persistent object database. A program declaresan object of this type to establish a database inwhich to store and from which to retrieve persis-tent objects.

Constructor Parody(const string& name)

Constructs the database object. The argument isthe file name of the database. The file name con-forms to the operating system's file name conven-tions. PARODY builds two physical files. The firsthas the file name extension .DAT and contains thepersistent objects. The second has the file nameextension .NDX and contains indexes into the per-sistent objects. This constructor opens the files ifthey exist and creates them if they do not.

Destructor '-Pa rody(

The Parody class destructor closes the databasefiles.

OpenDataBase static Parody *OpenDatabase()

Returns the address of the most recently declaredParody object.

Page 213: C++ Database Development 1558283579

Appendix B: PARODY Reference Guide 207

Persistent (parody.h)

class Persistent;

An abstract base class from which persistent objectclasses are derived.

Constructors Persistent()

Constructs a persistent object to be retrieved fromand stored in the most recently declared (and stillin scope) Parody database.

Persistent(Parody& db)

Constructs a persistent object to be retrieved fromand stored in the specified Parody database.

Read virtual void Read() = 0

Write virtual void Write() = 0

Functions provided by the derived class to readand write the data values of the persistent object.These functions call ReadObject and WriteObjectfunction templates for each of the data members.

LoadObject void LoadObject(ObjAddr nd = 0)

Called from the derived persistent object's con-structor to load the object into memory from thedatabase. If the nd parameter is nonzero, theobject is assumed to be keyless.

SaveObject void SaveObjecto)

Called from the persistent object's destructor to savethe object from memory into the PARODY database.

AddObject bool AddObject()

Tells PARODY to add a newly created object tothe database when the destructor calls SaveObject.

Page 214: C++ Database Development 1558283579

208 c++ Database Development

Returns false if the addition cannot be made dueto integrity violations.

ChangeObject bool ChangeObjecto)

Tells PARODY to write a changed object back tothe database when the destructor calls SaveObject.Returns false if the change cannot be made due tointegrity violations.

DeleteObject bool DeleteObject()

Tells PARODY to delete the object from the data-base when the destructor calls SaveObject.Returns false if the delete cannot be made due tointegrity violations.

ObjectExists bool ObjectExists()

Returns true if PARODY found the specifiedobject in the database. Valid after the object isinstantiated and after calls to the next six func-tions discussed.

FindObject Persistent& FindObject(PdyKey *key)

Finds the object associated with the value in thekey parameter.

CurrentObject Persistent& CurrentObject(PdyKey *key = 0)

FirstObject Persistent& FirstObject(PdyKey *key = 0)

LastObject Persistent& LastObject(PdyKey *key 0)

NextObject Persistent& NextObject(PdyKey *key 0)

PreviousObject Persistent& PreviousObject(PdyKey *key = 0)

Finds the object by position relative to the speci-fied key. If the key parameter is 0, uses the prima-

Page 215: C++ Database Development 1558283579

Appendix B: PARODY Reference Guide 209

ry key. If the class has no key (a keyless object),the retrieval uses the incidental position of theobject in the physical database.

ObjectAddress ObjAddr ObjectAddress(

Returns the object's address in the PARODY data-base.

Destroy static void Destroy(Persistent *pp)

Deletes the persistent object, which is assumed tohave been constructed from the free store. If thereare multiple instances of the object intercepted bya catch of the Persistent" exception, the deletedoes not take effect until the last of the instances isbeing destroyed.

PersistentObject (parody.h)

template <class T> PersistentObject:

A template class derived from the Persistent classthat encapsulates most of the persistence coopera-tion normally provided by derived persistent class-es. This template works with keyless objects thatare flat structures with no references or pointers.The using program instantiates objects of the tem-plate class. The class provides its own persistentconstructors, destructor, and Read and Write func-tions. The using program dereferences the objectvalue through the public Obj data member. Theprogram calls the following Persistent class mem-ber functions to manage the persistent object.

+ AddObject+ ChangeObject

+ DeleteObject

* ObjectExists+ FindObject

Page 216: C++ Database Development 1558283579

210 C++ Database Development

+ CurrentObject* FirstObject

* LastObject* NextObject* PreviousObject

* ObjectAddress

+ Destroy

Constructors PersistentObject(const T& obj)

Construct a PersistentObject<T> object from areference to a T object.

PersistentObject(ObjAddr oa = 0)

Construct a PersistentObject<T> object from anObjaddr value. If the value is nonzero, PARODYretrieves from the database the object located atthe specified object address.

Reference (parody.h)

template <class T> class Reference;

A template that encapsulates a persistent referencein one persistent object to another persistent objectin the database. The T type must be derived fromthe Persistent class, and the declaration of theReference<T> object must be a data member of aclass derived from the Persistent class. The usingclass dereferences the object through the Referencetemplate's public obj data member.

Constructor Reference()

Constructs a persistent reference data member in apersistent class. When an object of the class isinstantiated and read from the database, the refer-enced object-if one has been established-isinstantiated at the same time.

Page 217: C++ Database Development 1558283579

Appendix B: PARODY Reference Guide 211

ReadObject void ReadObject()

Used in the Read function of the Persistent derivedclass that contains the Reference<T> object toinstantiate and read the referenced object intomemory.

WriteObject void WriteObject()

Used in the Write function of the Persistentderived class that contains the Reference<T>object to write the referenced object to the database.

operator= void operator-(T& to)

The overloaded assignment operator establishesthe persistent reference. The using program assignsa persistent object of a matching type to theReference<T> object in the referencing object.

RemoveReference void RemoveReference()

Removes the persistent reference from the refer-encing object.

ScreenMenu (gui.h)

class ScreenMenu;

Implements a full screen menu.

Constructor ScreenMenu(const char *title, ..

Constructs a ScreenMenu object consisting of themenu title text and a variable list of argumentpairs. Each pair consists of a test for the menuselection and a function pointer. The functionpointers point to the functions to be called whenthe user chooses the corresponding selection fromthe menu.

Page 218: C++ Database Development 1558283579

212 C++ Database Development

Execute void Executeo)

Clears the screen, displays the menu, and waits forthe user to press a key. Each menu selection isnumbered starting with 1. A final selection is dis-played specifying the Esc key for the selection toexit from the menu. If the user presses a digit cor-responding to one of the selections, Execute callsthe corresponding function from the constructor'svariable argument list. When the called functionreturns, the Execute function repeats its processbeginning with the screen clear operation. If theuser presses Esc, the Execute function returnsimmediately. If the user presses any other key, thefunction sounds the audible alarm and waits foranother keypress.

Exceptions ThrownParody throws several exceptions. Most of them represent errors in theusing program. One is used to manage multiple copies of the same persis-tent object.

Program Error ExceptionsTable B.2 shows the PARODY program error exceptions.

EXCEPTION

TABLE B.2 PARODY program error exceptions

CONDITION CAUSING EXCEPTION TO BE THROWN

PdyExceptionsNotinConstructorNotlnDestructorNoDatabaseNotLoadedNotSavedMustDestroyZeroLengthKeyBadKeyLengthBadReferenceBadObjAddr

Not thrown. Base class for program error exceptions.ReadObject called from outside a persistent object constructor.WriteOblect called outside a persistent object destructor.Database operations attempted with no open database.

LoadObiect not called for a persistent object.SaveObject not called for a persistent object.

delete operator used on an object with multiple copies.

string object used for key with no length specified.

string object used for key with length different from that established for the index.

Declared Reference<T> object when T not derived from Persistent.

Bad object address provided to retrieve persistent object.

Page 219: C++ Database Development 1558283579

Appendix B: PARODY Reference Guide 213

PdyExceptions is not thrown. It is a base class from which the otherexception classes in Table B.2 are derived. You can catch the derivedexceptions individually or catch the PdyExceptions exception to catchthem all in the same place.

Persistent* ExceptionPARODY throws the Persistent* exception when the using programinstantiates a persistent object that is already in scope. The constructorthrows the exception, which means that the construction is not completed.The thrown variable contains the address of the existing persistent object.

GUI ExceptionsThe menu classes throw exceptions as listed in Table B.3

TABLE B.3 Menu program error exceptions

EXCEPTION CONDITION CAUSING EXCEPTION TO BE THROWN

NoGUI Declared a ScreenMenu or OneLineMenu object with no GUI object in scope.BadMenu Invalid argument list in ScreenMenu or OneLineMenu constructor call.

Disk Data StructuresThis section contains a technical description of the PARODY file struc-tures. You do not need to understand these structures in order to usePARODY, but knowledge of them is helpful when you get into detaileddatabase troubleshooting. Data structures on disk are not always obviousin view of the class definitions from which they are written. This discus-sion describes how PARODY organizes data on disk and the format ofeach physical record.

Disk FilesA PARODY database consists of two files: the data file, which containsthe objects; and the index file, which contains key indexes to the objects.

Page 220: C++ Database Development 1558283579

214 c++ Database Development

The Parody object opens or creates these two files when the programdeclares the object. The data file has the file extension .DAT, and theindex file has the file extension .NDX. The file architecture must support:

* Variable-length persistent objects.+ Indexes into and objects of multiple classes.+ Persistent objects that can grow or shrink in length or be

deleted between the time a program declares one and whenthe program destroys it.

+ Reuse of deleted file space.

To support these objectives, PARODY uses a system of file nodes,described next.

NodesBoth files in a PARODY database are managed by an input/output sys-tem of fixed-length file nodes in a thread. Nodes are logically addressedby node numbers starting with 1. PARODY uses the node system twoways. The index files are organized into fixed-length nodes, which inheritthe behavior of the Node class, declared in node.h. The objects are unfor-matted variable-length data streams that could extend beyond the bound-aries of a node, so they declare Node objects and copy their data valuesinto and out of the nodes.

Both files are represented by a File Header Block at the beginning ofthe file and a variable number of fixed-length Data Nodes, as shown inFigure B.1.

FILE HEADER BLOCK

The header record contains two node numbers. The first node numberpoints to the first node in a chain of deleted nodes. The second nodenumber points to the highest node that has been allocated. Headerrecords are implemented as objects of the FileHeader type, declared innode.h.

DATA NODES

Each file consists of a variable number of fixed-length nodes. The firstnode is node number 1, and so on. Each node begins with a NodeNbr

Page 221: C++ Database Development 1558283579

Appendix B: PARODY Reference Guide 215

variable that points to the next node in a logical list of nodes that form aset. The last node in the set has a value of zero in the NodeNbr variable.If the node has been deleted by its user, the set is a list of nodes the firstof which is pointed to by the deleted node pointer in the File Headerblock. Nodes are implemented as objects of the Node type, declared innode.h.

File Header Block Data Nodes

Next deleted node Next Node Data

Highest node

FIGURE B. I File header block and data nodes

NODE THREADS

A node thread consists of a number of nodes. PARODY maintains apointer to the first node in the thread. The node pointer at the front ofthe first node points to the second node, which points to the third, and soon, until a node is reached that has zero in its node pointer, which marksthe end of the thread. The nodes in a thread are in no particular sequencein the file.

DELETING NODES

When PARODY is finished with a node, it tells the Node class to deletethe node. This action clears the node's data area to zeros and adds it tothe deleted node thread, which is pointed to by the deleted node pointerin the header record. The pointer actually points to the most recentlydeleted node. At first, before any nodes have been deleted, the deletednode pointer is zero. When a node is deleted, its constructor moves thedeleted node pointer from the header into its next node pointer andmoves the deleted node's address into the deleted node pointer in theheader.

ALLOCATING NODESWhen PARODY needs a new node, it looks first at the deleted nodepointer in the header. If the pointer is nonzero, PARODY appropriatesthat node for the new usage and writes the next node pointer from thenode into the deleted node pointer.

Page 222: C++ Database Development 1558283579

216 c++ Database Development

If the deleted node thread has no nodes, PARODY appropriates anode from the end of the file by using the node number of the highestnode allocated as recorded in the file header.

Persistent ObjectsThe persistent object data file consists of a File Header Block and DataNodes. Each persistent object is stored in a set of nodes. Each node in theset has the NodeNbr variable mentioned above to form the set followedby the Object Header and Object Data, as shown in Figure B.2.

Object Header Object Data

Next Node Class ID Relative Node # Data Byte Stream

FIGURE B.2 Object header and data

OBJECT HEADER

The Object Header is an object of type ObjectHeader, declared inparody.h. It contains an integer class identification code for the objectfollowed by a relative NodeNbr variable for the object. The first rela-tive node number is zero, signifying that the node is the first node ofthe object. Subsequent relative node numbers are numbered sequen-tially. This pattern allows PARODY to identify the first node of anobject without an index reference to it and is used in rebuilding theindex file, described in Chapter 9.

OBJECT DATA

PARODY stores objects in node threads. Each object starts on a nodeboundary. The node number is the object's address. The data bytes of theobject follow the Object Header.

If the object's length is greater than that of a node, the object spillsover into the next node in the thread, continuing that way until the entireobject is recorded. The balance of the last node in an object's thread ispadded with zeros. Figure B.3 shows the relationship between the indexfile and the object file.

Page 223: C++ Database Development 1558283579

Appendix B: PARODY Reference Guide 217

I Null IClass ID I Node 2 1Data II

FIGURE B.3 Index and object file

The same physical file of nodes holds all the objects of all the classes in adatabase. Every class can support objects of different sizes and formats,and objects of a class can have variable lengths as well. A class that has astring data type might have objects with many different string lengths, forexample. A class with an array can have objects with different numbersof elements in the array.

Without the class definitions and the member functions that support theobjects, the object file is meaningless. Nothing that stands alone in the fileitself tells the database the types of the objects or anything about their for-mats. For PARODY to correctly locate an object, it needs the address of theobject's first node. That address is maintained by the indexes, described next.

IndexesThe database index file contains indexes that associate key values withobject node addresses in the object file. An index file holds one logicalindex table for every primary and secondary key in the persistent classes.The indexes are implementations of the B-tree algorithm.

The index file consists of a File Header Block and Data Nodes. Thereis a set of data nodes that contains the Index Header Records, and thereare individual one-node sets that contain the B-Tree Node Header and anarray of fixed-length key values.

CLASS HEADER TABLES

Node 1 in the index file is the first node in a set of nodes that containIndex Header Blocks for the persistent classes. Each Index Header Node

Page 224: C++ Database Development 1558283579

218 C++ Database Development

represents one persistent class. The node contains a fixed-length classname string object and an array of Index Header Blocks. The first nodein the set contains the array for class identification zero; the second setcontains the array for class identification one; and so on. The first entryin the array is for the primary key of the class; the second entry is forthe first secondary key; and so on. Figure B.4 shows the Index HeaderNode set.

Class ID 0Next Node I Ndx Hdr BIk-pri Ndx Hdr BIk-seclI Ndx Hdr Blk-sec2

I

Class ID 1 Next Node Ndx Hdr BIk-pri Ndx Hdr BIk-secli Ndx Hdr BIk-sec2

Class ID 2 E--1 Next Node I Ndx Hdr Bik-pri Ndx Hdr B3k-secl Ndx Hdr BIk-sec2

FIGURE B.4 Index Header Nodes

INDEX HEADER BLOCKSEach Index Header Block for a class contains two data items: the nodenumber of the root node for the index, and the length in bytes of theindex key value. The software assigns the root node number when theindex is first built. The key lengths are determined from the Key<T>objects.

B-TREES

PARODY uses the B-tree data structure to implement the indexes. The B-tree is an index structure that R. Bayer and E. McCreight developed in1970. It is a balanced tree of key values used to locate the object nodethat matches a specified key argument. The tree is a hierarchy of nodes inwhich each node contains from one to a fixed number of keys.

A B-tree consists of a root node and usually two or more lowernodes. If the total number of keys in the tree is equal to or less than thenumber that a node can contain, then only the root node exists. Whenthat number exceeds the capacity of a node, the root node splits into twolower nodes, retaining the key that is logically between the key values ofthe two new nodes. Higher nodes are parents of the lower nodes in thehierarchy.

Page 225: C++ Database Development 1558283579

Appendix H: PARODY Reference Guide 219

Nodes store keys in key value sequence. Each key consists of the keyindex value provided by the object and the object's node address in thedatabase provided by PARODY. When the tree has multiple levels, eachkey in a parent node points to the lower node that contains keys greaterthan the parent key and less than the next adjacent key in the parent. Thenodes at the lowest level are called leaves and do not contain pointers tolower nodes.

Figure B.5 is an example of a B-tree that uses the letters of the alpha-bet as keys to locate matching words from the phonetic alphabet. The let-ters are analogous to the data values for key data elements in a class; thephonetic alphabet words, incomplete in the example, are equivalent tothe objects that the B-tree indexes.

FIGURE B.5 The B-tree

When a node becomes overpopulated by key value insertions, the inser-tion algorithm splits it into two nodes and inserts the middle key valueinto the parent node of the original, unsplit node. If the root node splits,a new root node is grown as the parent of the split nodes.

When a node becomes underpopulated as the result of key value dele-tions, it combines with an adjacent node. The key value that is betweenthe two nodes and that is in the common parent node of the two ismoved into the combined node and deleted from the parent node. If the

Page 226: C++ Database Development 1558283579

220 C++ Database Development

deleted key is the last key in the root node, the old root node is deleted,and the newly combined node becomes the root node.

In the example in Figure B.5, the root node has one key value, andeach of the other nodes has two keys. This example makes for a simpleillustration of the structure, but, in practice, a typical B-tree has as manykeys as will fit in a node.

The B-tree algorithms provide that the tree always remains in bal-ance-that is, the number of levels from the root node to the bottom ofthe tree is always the same no matter which branch is taken by the search.This property gives the B-tree its name. The "B" stands for "balanced."

B-TREE NODE HEADER AND KEYS

The Data Nodes in a B-tree contain the B-tree Node Header block fol-lowed by a fixed-length array of keys.

The B-tree Node Header Block is in the format shown in its structurein Figure B.6.

strict TNodeHeader fbAke isleaf; c/ true if node is a leafNodeNbr parent; ro parent to this nodeNodeNbr leftsibling; ke left sibling nodeNodeNtsr rtghtsibltnge nd right sibling nodeint keycount; in number of keys in this nodeNodeanbr lowernode; s/ lower node associated with

keyss < keys in this node

FIGURE 0.6 The B-tree Node Header

A key in the array consists of its key data value followed by the NodeNbraddress of the object in the data file to which the key points. If the nodeis not a leaf node, each key array entry also includes a NodeNbr variablethat points to the B-tree node at the next lower level in the tree that con-tains keys with collating sequence values greater than the current keyvalue and less than the next adjacent key value in the array.

Page 227: C++ Database Development 1558283579

Appendix B: PARODY Reference Guide 221

ReferencesFundamentals of Data Structures, Horowitz and Sahni, 1976, ComputerScience Press, Inc.The Art of Computer Programming, Volume 3, Donald E. Knuth, 1973,Addison-Wesley

Page 228: C++ Database Development 1558283579

Appendix CTable of ContentsListings by CategoryGENERIC USER INTERFACE CLASSC .1 GUI.H ................................ 226C.2 GUI.CPP ................................ 229

LINKED LIST CLASS

C.3 LINKLIST.H ................................. 237

BOOL CLASS

C.4 BOOL.H ................................. 244

MONEY CLASS

C.5 MONEY.H ................................ 244C.6 MONEY.CPP ................................ 245

DATE CLASS

C.7 DATE.H ................................ 246C.8 DATE.CPP ................................ 247

223

Page 229: C++ Database Development 1558283579

224 c++ Database Development

PARODYC.9 PARODY.HC.10 PARODY.CPPC.11 KEY.HC.12 KEY.CPPC.13 BTREE.HC.14 BTREE.CPPC.15 NODE.HC.16 NODE.CPPC.17 TNODE.CPP

PAYROLL APPLICATION

C.18 PAYROLL.HC.19 PAYROLL.CPP

THE GAME OF LIFE

C.20 LIFE.CPP

PERSONNEL APPLICATION

C.21 PERSONEL.HC.22 PERSONEL.CPPC.23 EMPLOYEE.HC.24 EMPLOYEE.CPPC.25 DEPT.HC.26 DEPT.CPPC.27 PROJECT.HC.28 PROJECT.CPPC.29 ASSIGN.HC.30 ASSIGN.CPPC.31 MANAGER.HC.32 MANAGER.CPPC.33 PROJVIEW.CPP

EXAMPLE INDEX BUILDER

C.34 INDEX.CPP

248258280287288292305307311

320321

324

335335349350354356360362366368371372374

375

.................

o o. . . . . . . . . .o o . o . o .

S.. . . . . . . . . . . . . . .o o

.................

.................

.................

.................

.................

.................

.................

.................

.................

.................

.................

.°....°o...°°..oo

.................

.................

. o. °. . °. .° . .° . .

° . ° o o o o ° , o .

° o. ° ,.°. .°. ° °. °

o ,. .° . .° . .° . . o.

o o. ° °. °. . o. ° °. o

° . ° °. o. . °. ° o. °

° . ° °. °. . o. ° °. °

° o. ° °. °. . °. ° °. °

° . o o. o. . o. o o. °

° o. ° ° o o. . °. ° °. o

° o o ° °.°. . °. o o.

o o o o o o o o o°. .

Page 230: C++ Database Development 1558283579

Appendix C: Software Listings 225

FAMILY TREE APPLICATION

C.35 FAMILY.H ................................ 377C.36 FAMILY.CPP ................................ 378C.37 FAMTREE.CPP ................................ 380

Listings Alphabetically by File NameNAME LISTING PAGE

ASSIGN.CPP C.30 ................................ 368ASSIGN.H C.29 ................................ 366BOOL.H C.4 ................................ 244BTREE.CPP C.14 ................................ 292BTREE.H C.13 ................................ 288DATE.CPP C.8 ................................ 247DATE.H C.7 ................................ 246DEPT.CPP C.26 ................................ 356DEPT.H C.25 ................................ 354EMPLOYEE.CPP C.24 ................................ 350EMPLOYEE.H C.23 ................................ 349FAMILY.CPP C.36 ................................ 378FAMILY.H C.35 ................................ 377FAMTREE.CPP C.37 ................................ 380GUI.CPP C.2 ................................ 229GUI.H C.1 ................................ 226INDEX.CPP C.34 ................................ 375KEY.CPP C.12 ................................ 287KEY .H C.11 ................................ 280LIFE.CPP C.20 ................................ 324LINKLIST.H C.3 ................................ 237MANAGER.CPP C.32 ................................ 372MANAGER.H C.31 ................................ 371MONEY.CPP C.6 ................................ 245MONEY.H C.5 ................................ 244NODE.CPP C.16 ................................ 307NODE.H C.15 ................................ 305PARODY.CPP C.10 ................................ 258

Page 231: C++ Database Development 1558283579

226 c++ Database Development

PARODY.HPAYROLL.CPPPAYROLL.HPERSONEL.CPPPERSONEL.HPROJECT.CPPPROJECT.HPROJVIEW.CPPTNODE.CPP

11 //21

31 #i

41 #d

5I61 //71 / /

81 //

91101 #i

11 #i121 #i

131 #i

141 #i

151 #i

161

C.9C.19C.18C.22C.21C.28C.27C.33C.17

- gui.h

fndef GUIH

efine GUIH

Generic User Interface (GUI)

ncl ude

ncl ude

ncl ude

ncl ude

ncl ude

ncl ude

<iostream.h>

<stdarg.h>

<cstring.h>

"bool .h"

"date.h""money.h"

// ------ exceptions

class BadMenu{};

class NoGUI{};

// --------- screen dimensions

const short int SCREENHEIGHT = 25;

const short int SCREENWIDTH = 80;

248321320335335362360374311

Listings

LISTING C.A GUI.H

171

181

191201

211

221

231

Page 232: C++ Database Development 1558283579

Appendix C Software Listings 227

241

251 // -------- function key values

261 const short int UPARROW - 200;

271 const short int DOWNARROW - 208;

281 const short int RIGHTARROW - 205;

291 const short int LEFTARROW = 203;

301 const short int ESC - 27;311

321 // ---------- the Generic User Interface class

331 class GUI {

341 char putback;

351 public:

361 GUI()

371 t putback - 0; gui = this; }381 -GUI()

391 { gui -; }401 /--- data field entry

411 void Userlnput(string *s, const char *prompt, short len);

421 void Userlnput(char *c, const char *prompt);

431 void Userlnput(short int *i, const char *prompt);

441 void Userlnput(long int *i, const char *prompt);451 void Userlnput(Date *dt, const char *prompt);461 void Userlnput(Money *m, const char *prompt);

471 // ---- standard dialogs481 bool YesNo(const char *question);

491 void Error(const char *message) const;501 void AnyKeyo) const;

511 // --- screen operations

521 void SetCursor(short int x, short int y) const;

531 void ClearScreeno) const;

541 void WriteChar(char c, short x, short y) const;551 void StatusLine(const string& s) const;561 // ------ keyboard operations

571 bool KBCharWaiting() const;581 unsigned char GetKBChar() const;

591 void PutBack(char c);601 static GUI *gui;

611 ;

Page 233: C++ Database Development 1558283579

228 c++ Database Development

621

631 typedef void (*Mfunc)();

641 // --.-- maximum commands in a menu

651 const short int MAXCOMMANDS = 9;

661 // where to display menus

671 const short int menux - (SCREENWIDTH/2-SCREENWIDTH/4);

681 const short int menuy = (SCREENHEIGHT/2-SCREENHEIGHT/4);

69

701 /

711 I Generic Menu Class

721 /

731 class Menu {

741 protected:

751 Mfunc sels[MAXCOMMANDS];

761 char selcodes[MAXCOMMANDS];

771 short int selection;781 short int cmdno;

791 void Select(bool clearscreen = false);801 public:

811 Menu() throw (NoGUI);

821 virtual void Executeo) = 0;

831 1;

84

85? class ScreenMenu : public Menu

861 string title;

871 string labels[MAXCOMMANDS];88? public:

891 ScreenMenu(const char *title ) throw (BadMenu);

901 void Executeo);

91 1;

921

931 class OneLineMenu : public Menu f

941 short int cmdct;

95? string menuline;

961 public:

971 OneLineMenu(const char *menu, ) throw (BadMenu);981 void Execute();

99? };

Page 234: C++ Database Development 1558283579

Appendix C: Software Listings 229

1001

1011 #endif

LISTING C.2 GUI.CPP

1 //I-

21

31 /

4 // Generi

51 /

61

71 #include

81 #include

91 #include101 #include

111 #include121 #include

131

141 GUI *GUI:

151

161 ------171 Menu u

181 ------191 Menu::Mer

201 t

211 selec

221 cmdnc

231 if (G241 t

251 1

261

271 void Menu

281

291 for

301 u311 i

321

331

gui .cpp

c User Interface (GUI)

<iostream.h>

<iomanip.h>

<stdarg.h>

<ctype.h>

<conio.h>"gui .h"

:gui;

ser interface

u() throw (NoGUI)

tion - 0;

= 0;UI::gui == 0)

.hrow NoGUIO;

i::Select(bool clearscreen)

;;) {unsigned char sl = GUI::gui->GetKBChar();

f (isprint(sl))

cout << sl << '\b';

Page 235: C++ Database Development 1558283579

230 C++ Database Development

341 if (sl == ESC) t

351 selection = ESC;

361 break;

371 }381 // --- search for a match among the selections

391 for (selection = 0; selection < cmdno; selection++) {

401 if (selcodes[selection] == tolower(sl)) f

411 // --- execute the command

421 if (clearscreen)

431 GUI::gui->ClearScreen();

441 (*sels[selection])();

451 break;

461

471 }

481 if (selection < cmdno)

491 break;

501 cout << '\a';511

521 1

531541 // -

551 / full screen menu user interface

561 /--------------------------------

571 ScreenMenu::ScreenMenu(const char *ttl.

581 throw (BadMenu)

591 {601 title = ttl;

611 va list ap;

621 va start(ap, ttl);

631 char cc = '1';

641 // ---- gather the menu from the caller's parameters

651 while (cmdno < MAXCOMMANDS) t

661 char *sp = va arg(ap, char*);

671 if (sp == 0)

681 break;

691 labels[cmdno] = sp;701 selcodes[cmdno] = cc++;

711 sels[cmdno] = va arg(ap, Mfunc);

Page 236: C++ Database Development 1558283579

Appendix C Software Listings 231

721 if (sels[cmdno++] == 0)

731 throw BadMenuo;

741

75176

77 void ScreenMenu::Execute(781 [

791 selection = 0;

801 while (selection != ESC) {811 GUI::gui->ClearScreen( ;

821 GUl::gui->SetCursor(menux, menuy);

831 // display the menu

841 cout << title;

851 for (short int i = 0; i < cmdno; i++) [

861 GUI::gui->SetCursor(menux+2, menuy+2+i);

871 cout << (i+1) << ": " << labels[i];

881 1

891 GUI::gui->SetCursor(menux, menuy+2+i);

901 cout << "Esc: Return";

911921 // get the user's selection

931 GUI::gui->SetCursor(menux, menuy+4+i);941 cout << "Select>

951 Select(true);961

971 1

981991 ---------------------------------

1001 / one line menu user interface1011 //

1021 OneLineMenu::OneLineMenu(const char *menu ... )

1031 throw (BadMenu)

1041 {

1051 menuline = menu;

1061 // collect and count the menu commands

1071 const char *cp = menu;

1081 cmdct = 0;

109! for (short sel = 0; sel < MAXCOMMANDS && *cp; sel++)

Page 237: C++ Database Development 1558283579

232 c++ Database Development

1101 while (*cp && isspace(*cp))

1111 cp++;

1121 selcodes[cmdct++] = tolower(*cp);

1131 while (*cp && !isspace(*cp))

1141 cp++;

1151 }

1161 // gather the menu functions

1171 valist ap;

1181 va-start(ap, menu);

1191

1201 while (cmdno < cmdct) {

1211 sels[cmdno] = va-arg(ap, Mfunc);

1221 if (sels[cmdno++] == 0)

1231 throw BadMenuo);

1241

1251

1261

1271

1281 void OneLineMenu::Execute()

1291 {

1301 --- display the menu

1311 cout << endl << menuline <<

1321

1331 selection = 0;

1341 // --- get the user's selection

1351 Select();

1361 1

1371

1381 //

1391 / character input

1401 //

1411 void GUI::Userlnput(char *c, const char *prompt)

1421 f

1431 cout << endl << prompt <<

1441 cout.flusho);

1451 *c - GetKBChar();

1461 1

1471

Page 238: C++ Database Development 1558283579

Appendix C: Software Listings 233

148

1491

1501

151

1521

1531

1541

1551

1561

1571

1581

1591

1601

1611

1621

1631

1641

1651

1661

1671

1681

1691

1701

1711

1721

1731

1741

1751

1761

1771

1781

1791

1801

1811

1821

1831

1841

1851

//

// Date input

//

void GUI::Userlnput(Date *dt, const char *prompt)

cout << end] << prompt << " (mm dd yy):

cout.flush();

unsigned short mo, da, yr;

cin >> ws >> mo >> da >> yr;*dt = Date(mo,da,yr);

- -----

// Money input

//-------

void GUI::Userlnput(Money *m, const char *prompt){

cout << endl << prompt << ": ";

//

// short integer input

//

void GUI::Userlnput(short int *i, const char *prompt){

cout << endl << prompt <<

cout.flush();

cin >> ws >> *i;

II ---- - - - - - - - - - - - - - - -

// long integer input

//---

void GUI::Userlnput(long int *I, const char *prompt)

cout << endl << prompt <<

cout.flush();

cin >> ws >> *I;}

t

}

Page 239: C++ Database Development 1558283579

1861

1871

1881

1891

1901

1911

192!

193!

194!

1951

196!

197

1981

199!

200!

2011

2021 }

2031

204! -//

2051 / yes/no

206! //

2071 bool GUI::

2081 f

2091 char c

2101 while

2111 co

2121 co

2131 c

2141 c

2151 if

2161

217!

218! cout.f

219! return

2201 1

221!

222! -- --

2231 / error m

user interface

YesNo(const char *question)

(c !- 'y' && c 1= n)

ut << endl << question <<

ut.flush();

= GetKBCharO;

= tolower(c);

(c 1= 'y' && c 1= n)

cout << '\a';

"?"? (y~n)

lush();

c == 'y

essage

234 c++ Database Development

cout.flush();cin >> ws >> m->Valueo;

// string input

------ - ----

void GUI::Userlnput(string *s, const char *prompt, short len)

fcout << endl << prompt << ..

cout.flusho;

char *bf - new char[len+1l;

cin >> ws;

cin.getline(bf, len+1);*s = bf;

s->resize(len);

Page 240: C++ Database Development 1558283579

Appendix C Software Listings 235

2241 //

2251 void GUI::Error(const char *message) const

2261 {

2271 cout << endl << '\a' << message << endl;

2281 AnyKeyo);

2291 }

2301

2311 //

2321 / get a keyboard char

2331 //

234j unsigned char GUI::GetKBChar() const

2351

2361 short int c;

2371 if (putback) {

2381 c = putback;

2391 (const-cast<GUI*>(this))->putback - 0;

2401 1

2411 else

2421 c = getcho;

2431 if (c == 0)

2441 // --- convert function key

2451 c = getch( I Ox80;

2461 else

2471 // --- strip scan code

2481 c &= Oxff;

2491

2501 return static cast<unsigned char>(c);

2511 12521

2531 / -----

2541 / put back a keyboard char

2551 //

2561 void GUI::PutBack(char c)

2571

2581 putback = c;

2591 )

2601

2611 //

Page 241: C++ Database Development 1558283579

236 c++ Database Development

2621 // Display a status line

2631 //

2641 void GUI::StatusLine(const string& s) const

2651 {

2661 SetCursor(O, SCREENHEIGHT-1);

2671 cout << S;

2681 cout.flusho);

2691 short int len = strlen(s.c-stro);

2701 while (len++ < SCREENWIDTH-1)

2711 cout << « ;

2721 cout.flush( ;

2731 }

2741

2751

2761 //

2771 / Prompt for and read any key

2781 /---------

2791 void GUI::AnyKey() const

2801 (

2811 cout << endl << "Any key..." << endl;

2821 GetKBChar();

2831 1

2841

2851 //

2861 / Position the cursor

2871 //

2881 void GUI::SetCursor(short int x, short int y) const

2891 {

2901 cout << "\033[" << (y+l) << ';' << (x+1) << H';

2911 cout.flusho;

2921 1

2931

2941 //

2951 / Clear the screen

2961 // ---

2971 void GUI::ClearScreen() const

2981 {

2991 cout << "\033[2J";

Page 242: C++ Database Development 1558283579

3001

301

3021

303

3041

3051

3061

307

3081

3091

3101

3111

3121

3131

3141

3151

3161

3171

3181

3191

LISTING C.3 LINKLIST.H

// linklist.h

// a template for a linked list

#ifndef LINKLIST H

#define LINKLISTH

7 // --- the linked list entry

81 template <class T>

91 class ListEntry {

10 friend class LinkedList<T>;

ill T *thisentry;

121 ListEntry<T> *nextentry;

131 ListEntry<T> *preventry;

141 ListEntry(T *entry);

151 1;

Appendix C: Software Listings 237

cout.flush();}

//

// Write a single character to the screen

//void GUI::WriteChar(char c, short int x, short int y) const{

SetCursor(x, y);

cout << c;

cout.flush();

-- - - - -- - - - -- - - - - - --

// Test for a keyboard character waiting

// ------

bool GUI::KBCharWaiting() const

{return kbhit();

}

1i

2131

41

5161

Page 243: C++ Database Development 1558283579

238 c++ Database Development

16

171 template <class T>

181 // --- the linked list

191 class LinkedList f

201 // --- the listhead

211 ListEntry<T> *firstentry;

221 ListEntry<T> *lastentry;

231 ListEntry<T> *iterator;

241 T *CurrentEntryo);

251 void RemoveEntry(ListEntry<T> *entry);

261 void InsertEntry(T *entry);

271 void InsertEntry(T *entry. short int pos);

281 void RemoveEntry(short int pos);

291 public:

301 LinkedList( ;

311 virtual -LinkedList()

321 { ClearList(); )

331 void AppendEntry(T *entry);

341 void InsertEntry(T *entry, T *curr);

351 void RemoveEntry(T *entry);

361 T *FindEntry(short int pos);

371 short int FindEntry(T *entry);

381 T *FirstEntry();

391 T *LastEntry();

401 T *NextEntry();

411 T *PrevEntry();

421 T *NextEntry(T *entry);

431 T *PrevEntry(T *entry);

441 void ClearListo;

451 J;

461

47 template <class T>

481 // ---- construct a linked list

491 LinkedList<T>::LinkedList()

501 f

511 iterator - 0;

521 firstentry - 0;

531 lastentry - 0;

Page 244: C++ Database Development 1558283579

Appendix C Software Listings 239

541

55156 template <class T>

57 // -- remove all entries from a linked list

581 void LinkedList<T>::ClearList()

591 f

601 ListEntry<T> entryy = firstentry;

611 while (lentry != 0) {

621 ListEntry<T> *nxt = lentry->nextentry;

631 delete lentry;

641 lentry = nxt;

651661 iterator = 0;671 firstentry 0;

681 lastentry = 0;

691

701

711 / - construct a linked list entry721 template <class T>

731 ListEntry<T>::ListEntry(T *entry)

74 1

751 thisentry = entry;

761 nextentry = 0;

771 preventry = 0;

781 1

791

801 template <class T>811 // -- append an entry to the linked list

821 void LinkedList<T>::AppendEntry(T *entry)

831 {

841 ListEntry<T> *newentry = new ListEntry<T>(entry);

851 newentry->preventry = lastentry;

861 if (lastentry)871 lastentry->nextentry = newentry;

881 if (firstentry = 0)

891 firstentry = newentry;

901 lastentry = newentry;

911 1

Page 245: C++ Database Development 1558283579

template <class T>

-- return the current linked list entry*LinkedList<T>: :CurrentEntry()

return iterator ? iterator->thisentry : 0;

921

931 t

941 /951 T

961 {

971

981 }

9911001 t

101 /,

1021 T

1031 f

1041

1051

1061 1

1071

1081 t

1091 /

1101 T

1111 f1121

1131

1141

1151

1161 ti

1171 /

1181 T

1191 t

in the linked list

iterator = firstentry;

return CurrentEntry();

template <class T>

--- . return the last entry*LinkedList<T>::LastEntry()

in the linked list

iterator = lastentry;

return CurrentEntryo;

template <class T>

/ -- return the next entry*LinkedList<T>::NextEntry(T

following the specified one*entry)

FindEntry(entry);

return NextEntryo:

j

template <class T>

// ---- return the next entry in the linked list

T *LinkedList<T>::NextEntry()

tif (iterator == 0)

iterator = firstentry;

240 C++ Database Development

template <class T>

/ return the first entry*LinkedList<T>::FirstEntry()

1201

1211

1221

1231

1241

1251

1261

1271

1281

1291

Page 246: C++ Database Development 1558283579

Appendix C Software Listings 241

1301 else1311 iterator - iterator->nextentry;1321 return CurrentEntry();133113411351 template <class T>1361 // -- return the previous entry ahead of the specified one1371 T *LinkedList<T>::PrevEntry(T *entry)1381 {1391 FindEntry(entry);1401 return PrevEntry();1411 114211431 template <class D>1441 // -- return the previous entry in the linked list1451 T *LinkedList<T>::PrevEntry()1461 {147! if (iterator == 0)1481 iterator - lastentry;

1491 else1501 iterator = iterator->preventry;1511 return CurrentEntryo);1521 11531154! template <class D>1551 // remove an entry from the linked list by position1561 void LinkedList<T>::RemoveEntry(short int pos)1571 {1581 FindEntry(pos);1591 if (iterator != 0)1601 RemoveEntry(iterator);1611 }16211631 template <class T>1641 // remove an entry from the linked list by entry address1651 void LinkedList<T>::RemoveEntry(ListEntry<T> *lentry)166!1671 if (lentry -= 0)

Page 247: C++ Database Development 1558283579

242 c++ Database Development

1681169

1701

1711

1721

1731

1741

1751

1761

1771

1781

1791

1801

1811

1821

1831

1841

1851

1861

187

1881

1891

1901

19111921

1931

1941

1951

1961

197

1981

1991

20012011

2021

2031

2041

2051

I

template <class T>

// remove an entry from the linked list by entry

void LinkedList<T>::RemoveEntry(T *entry)

{FindEntry(entry);

RemoveEntry(iterator);

}

template <class T>

// insert an entry into the linked list ahead of anothervoid LinkedList<T>::InsertEntry(T *entry, T *curr)

fFindEntry(curr);

InsertEntry(entry);

}

template <class T>

// insert an entry into the linked list by position

void LinkedList<T>::InsertEntry(T *entry, short int pos)

I

FindEntry(pos);

InsertEntry(entry);

return;

if (lentry == iterator)

iterator = lentry->preventry;

---- repair any break made by this removal

if (lentry->nextentry)

lentry->nextentry->preventry = lentry->preventry;

if (lentry->preventry)

lentry->preventry->nextentry = lentry->nextentry;S--- maintain listhead if this is last and/or first

if (lentry == lastentry)

lastentry = lentry->preventry;

if (lentry == firstentry)

firstentry = lentry->nextentry;

delete entry;

Page 248: C++ Database Development 1558283579

Appendix C: Software Listings 243

2061207

2081 template <class T>

2091 // -- insert an entry into the linked list ahead of iterator

2101 void LinkedList<T>::InsertEntry(T *entry)

2111

2121 if (iterator 0- 0)

2131 AppendEntry(entry);

2141 else {

2151 ListEntry<T> *newentry = new ListEntry<T>(entry);

2161 newentry->nextentry - iterator;

2171 if (iterator) f

2181 newentry->preventry - iterator->preventry;

2191 iterator->preventry = newentry;

2201 1

2211 if (newentry->preventry)2221 newentry->preventry->nextentry - newentry;

2231 if (iterator -- firstentry)

2241 firstentry = newentry;

2251 iterator - newentry;

2261227 1

2281

2291 template <class T>

2301 // return a specific linked list entry

2311 T *LinkedList<T>::FindEntry(short int pos)2321 t

2331 iterator - firstentry;

2341 while (iterator && pos--)

2351 iterator = iterator->nextentry;

2361 return CurrentEntry(;

2371 1

23812391 template <class T>

2401 // --- return a specific linked list entry number

2411 short int LinkedList<T>::FindEntry(T *entry)

2421 t

2431 short int pos = 0;

Page 249: C++ Database Development 1558283579

244 C++ Database Development

2441 if (entry != 0) t

2451 iterator = firstentry;

2461 while (iterator) {

2471 if (entry == iterator->thisentry)

2481 break;

2491 iterator = iterator->nextentry;

2501 pos++;

2511 }

2521

2531 return pos;

2541

2551

2561 #endif

LISTING C.4 BOOL.H

1 // ---------- bool .h

21

31 #ifndef BOOL H

41 #define BOOL H

61

718j91

101

111

// --- Boolean type

class bool {

unsigned char state;

public:

bool(int st = 0) { state = !Hst; Ioperator into const { return state; I

121 1

131

141 const

151 const

161

17J #endif

bool true = 1:bool false = 0;

LISTING C.5 MONEY.H

11 /I ---- money.h

21

Page 250: C++ Database Development 1558283579

Appendix C: software Listings 245

31 #ifndef MONEY H

41 #define MONEY H

5I

61 #include <iostream.h>71

81 class Money t

91 float value;

101 unsigned char wd; // display width

111 public:

121 Money(float v=O. unsigned char w = 6);

131 float& Value()

141 { return value; }

15i int operator<(const Money& m) const

161 { return value < m.value; }

171 int operator-=(const Money& m) const

181 t return value - m.value; I

191 int operator!=(const Money& m) const

201 { return value != m.value; }

211 int operator>(const Money& m) const

221 { return value > m.value; }

23 int operator<=(const Money& m) const

241 { return value <= m.value; }

251 int operator>=(const Money& m) const

261 f return value >= m.value; J

271 friend ostream& operator<<(ostream&osconst Money& m);

281 ;

291

301 #endif

LISTING C.6 MONEY.CPP

11 // ------ money.cpp

21

3 #include "money.h"

4 #include <math.h>51

61 --- construct Money from float

71 / adjust to even cents

Page 251: C++ Database Development 1558283579

Money::Money(float v, unsigned char w)

{float vl - fabs(v)

long dol - vl;

unsigned short ct

value - ct;

value /1 100;

value +- dol;

if (v < 0)

value *- -1;

wd - w;

- (vl - dol) * 100;

8191

10111i121

131

141

151

161171

181

191201

21122

231

241

251

261

271

281

291

301

// ----- date.h

31 #ifndef DATE H41 #define DATE H

5161 #include <iostream.h>

7181 typedef unsigned char DtEl;

91

class Date fDtEl mo, da. yr;

public:

246 c++ Database Development

// ---- ostream Money insertion operatorostream& operator<<(ostream&os,const Money& m)f

os << '$';

os.width(m.wd);os.precision(2);

os.setf(ios::fixed I ios::right);

os << m.value;

return os;}

LISTING C.7 DATE.H

1121

10111i

121

Page 252: C++ Database Development 1558283579

131141

151

161

171

181191

201

211

221

231

241

251

261

271

281

291

301

311

321

331

341

351

361

371

381

391 1

401

411

421 #end

Date(DtEl m=O,DtEl d=0,DtEl y=O) mo(m),da(d),yr(y){ I* ... *I }

DtEl Month() const

{ return mo; I

DtEI Day() const

t return da; }DtEl Year() const

{ return yr; I

void SetMonth(DtEl m)

t mo = m; I

void SetDay(DtEl d)

( da = d; )

void SetYear(DtEl y)

t yr = y; I

int operator<(const Date& dt) const;int operator==(const Date& dt) const

( return mo == dt.mo && da -= dt.da && yr == dt.yr;

int operator!-(const Date& dt) const

t return !(*this -- dt); Iint operator>(const Date& dt) const

[ return !(*this - dt I1 *this < dt); I

int operator<=(const Date& dt) const

t return (*this == dt I1 *this < dt); I

int operator>=(const Date& dt) const

t return (*this =- dt I1 *this > dt);friend ostream& operator<<(ostream&os, const Date& dt);

dif

LISTING C.8 DATE.CPP

1 // ---------- date.cpp21 #include "date.h"

41 // - overloaded relational less-than

5 int Date::operator<(const Date&dt) const

Appendix C: Software Listings 247

I

Page 253: C++ Database Development 1558283579

248 C++ Database Development

61

71 if (yr < dt.yr)

81 return 1;

91 if (yr -= dt.yr) {101 if (mo < dt.mo)

ill return 1;121 if (mo - dt.mo)

131 return da < dt.da;

141

151 return 0;

161 1

171181 // -.-- ostream insertion operator

191 ostream& operator<<(ostream&os, const Date& dt)

201 t211 os.setf(ios::right);

221 os.fill(' ');

231 os.width(2);

241 os << static cast<unsigned short>(dt.mo) << ;

251 os.fill('O');

261 os.width(2);

271 os << static cast<unsigned short>(dt.da) << '/';

281 os.width(2);

29 os << static cast<unsigned short>(dt.yr);

301 os.fill( ');

311 return os;

321 }

LISTING C.9 PARODY.H

11 // parody.h

21

31 #ifndef DATABASE H

41 #define DATABASEH

5I

61 #include <fstream.h>

71 #include <typeinfo.h>81 #include <cstring.h>

Page 254: C++ Database Development 1558283579

Appendix Software Listings 249

91101 -I

Il // Parody exceptions representing program errors

121 I

131 class PdyExceptions {};

141 // --- ReadObject from non-ctor

151 class NotInConstructor : public PdyExceptions ti;

161 // --- WriteObject from non-dtor

171 class NotlnDestructor : public PdyExceptions t);

181 // --- No database open

191 class NoDatabase : public PdyExceptions 0;

201 // --- LoadObject was not called

211 class NotLoaded : public PdyExceptions {};

221 // --- SaveObject was not called

231 class NotSaved : public PdyExceptions t);

241 // --- Multireference object deleted

251 class MustDestroy : public PdyExceptions HI;

261 // --- string key w/out size

271 class ZeroLengthKey public PdyExceptions {);

281 // --- Key length btree key length

291 class BadKeylength public PdyExceptions f);

301 // --- Bad reference object

311 class BadReference : public PdyExceptions };

321 // --- Bad ObjAddr specified

331 class BadObjAddr public PdyExceptions H};

341

351 //

361 / Class Identification

371

381 typedef short int ClassID;

391

401 /

411 / Class Identification structure

421//

431 struct Class {

441 char *classname;

451 ClassID classid;

461 streampos headeraddr;

Page 255: C++ Database Development 1558283579

Class(char *cn = 0) :classname(cn), classid(O), headeraddr(O)

{ /* ... */ }

Key Controls

pedef short int IndexNo;pedef short int KeyLength;

include "bool.h"

include "linklist.h"nclude "btree.h"

Object Address

ruct ObjAddr {

NodeNbr oa;ObjAddr(NodeNbr nd = 0)

1 /* ... */ I

operator NodeNbr() constf return oa; J

: oa(nd)

Persistent Object Header Rcd

581 #ir

591 #i1

601 #il611

621 //631 //641 //

651 st

661

671

681

691

701

711 ;

721

731 //741 //751 //761 st

771781

791801

81J };

821

831 /

841 /

// class identification// relative node number within object

classid(O), ndnbr(O)

Persistent object abstract base class

250 C++ Database Development

471

481

491

501 ;

511

521 /

531 /

541 /

ty

ty

551

561

571

ruct ObjectHeader

ClasslD classid;

NodeNbr ndnbr;

ObjectHeader()f /* .. */

Page 256: C++ Database Development 1558283579

Appendix C: Software Listings 251

//

class Persistent {

friend class Parody;

friend class PdyKey;

friend class PdyReference;

851

861

871

88189190j

911921

93194I

951961

971981

99I1001

101l

1021

1031

1041

1051

1061

1071

1081

109l

1101

il II1121

113I1141

115I

1161

1171

1181

1191

1201

1211

1221

I-I-

II

I-

II

I-

II

II

I-

II

Node address for this object

database for this object

number of keys in the object

number of instances of object

current node for reading/writing

current char position

true if user changed the objecttrue if user deleted the object

true if user is adding the object

true if LoadObject called

true if SaveObject called

true if object built with new

for saving file position

II --- pointers to associate keys with objects

Persistent *prevconstructed;

static Persistent *objconstructed;

static Persistent *objdestroyed;

LinkedList<PdyKey> keys;

LinkedList<PdyKey> orgkeys; // original keys in the object

// --- private copy constructor & assignment prevent copies

Persistent(Persistent&) : parody(Parodyo))

{ /* ... */ )

Persistent& operator-(Persistent&)

{ return *this; I

---- methods used from within Persistent class

void RegisterKey(PdyKey *key)

{ keys.AppendEntry(key); I

void ObjectOuto);

ObjectHeader objhdr;

ObjAddr objectaddress;

Parody& parody;

short int indexcount;

short int instances;

Node *node;

short int offset;

bool changed;

bool deleted;

bool newobject;

bool loaded;

bool saved;

static bool usingnew;

streampos filepos;

----------- --------------

Page 257: C++ Database Development 1558283579

252 C++ Database Development

1231 void RecordObject);

1241 void RemoveObjecto;

1251 void RemoveOrgKeys();

1261 void Addlndexeso;

1271 void Deletelndexeso;

1281 void Updatelndexeso;

1291 void PositionNode() throw (BadObjAddr);

1301 void ReadObjectHeader);

1311 void WriteObjectHeader();

1321 void Searchlndex(PdyKey *key);

1331 void ReadDataMemberso;

1341 PdyBtree *Findlndex(PdyKey *key);

1351 bool TestRelationships();

1361 void ScanForward(NodeNbr nd);1371 void ScanBackward(NodeNbr nd);

1381 void BuildObject() throw (NoDatabase);

1391 void TestDuplicateObjecto) throw (Persistent*);

1401 public:

1411 / --- These are public members because template functions1421 / cannot be friends or member functions, and some

1431 / template functions need to call them.1441 / Users should not call these member functions.

1451 static Persistent *ObjectBeingConstructed()1461 throw (NotlnConstructor);

1471 static Persistent *ObjectBeingDestroyedo)

1481 throw (NotlnDestructor);

1491 void PdyReadObject(void *buf, short int length);1501 void PdyWriteObject(const void *buf, short int length);

1511 void ReadStrObject(string& str);

1521 void WriteStrObject(const string& str);1531 // --- s/b called only from Reference template

1541 void AddReference()

1551 { instances++; }

1561 protected:

1571 Persistento;

1581 Persistent(Parody& db);1591 virtual -Persistent()1601 throw (NotLoaded, NotSaved, MustDestroy);

Page 258: C++ Database Development 1558283579

Appendix C Software Listings 253

1611 --- provided by derived class

1621 virtual void Write() = 0;

1631 virtual void Read() = 0;

1641 // --.- called from derived class's constructor1651 void LoadObject(ObjAddr nd = 0);

1661 public:

1671 -- - called from derived class's destructor

1681 / or by user to force output to database

1691 void SaveObject() throw (NoDatabase);

170

1711 --- class interface methods for modifying database

1721 bool AddObject();

1731 bool ChangeObject);

1741 bool DeleteObjecto;

1751 bool ObjectExists() const1761 t return objectaddress !0 0; 1

1771

1781 / .-- class interface methods for searching database1791 Persistent& FindObject(PdyKey *key);

1801 Persistent& CurrentObject(PdyKey *key = 0);

1811 Persistent& FirstObject(PdyKey *key 0);

1821 Persistent& LastObject(PdyKey *key = 0);

1831 Persistent& NextObject(PdyKey *key = 0);

1841 Persistent& PreviousObject(PdyKey *key = 0);1851 // --- return the object identification

1861 ObjAddr ObjectAddress() const

1871 { return objectaddress; }

1881 // pseudo delete operator for multiple instances

1891 static void Destroy(Persistent *pp);

1901 1;

1911

1921 // ...

1931 / DataFile class

1941 /

1951 class DataFile : public NodeFile {

1961 public:

1971 DataFile(const string& name) : NodeFile(name+".dat")

1981 { /* ... */ I

Page 259: C++ Database Development 1558283579

254 c++ Database Development

1991 1;

2001

2011 -//

2021 / the Parody database

2031 /

2041 class Parody t

2051 friend Persistent;

2061 DataFile datafile; // the object datafile

2071 IndexFile indexfile; // the b-tree file

2081 LinkedList<Persistent> objects; // instantiated objects

2091 LinkedList<Class> classes; 1/ registered classes

2101 LinkedList<PdyBtree> btrees; // btrees in the database

2111 // --- for Index program to rebuild indexes

2121 ObjAddr rebuildnode; // object being rebuilt

2131 Parody *previousdatabase; // previous open database

2141 static Parody *opendatabase; // latest open database

2151 void GetObjectHeader(ObjAddr nd, ObjectHeader& objhdr);

2161 void Rebuildlndexes(ObjAddr nd)

2171 t rebuildnode - nd; }

2181 bool FindClass(Class *cls, NodeNbr *nd - 0);

2191 ClasslD GetClassID(const char *classname);

2201 friend void Buildlndexo);

2211 // --- private copy constructor & assignment prevent copies

2221 Parody(Parody&) : datafile(string()), indexfile(string())

2231 { /* ... */ 1

2241 Parody& operator-(Parody&)

2251 t return *this; J

2261 void Registerlndexes(Class *cls, const Persistent& pcls)

2271 throw (ZeroLengthKey);

2281 ClasslO RegisterClass(const Persistent& cls);

2291 Class *Registration(const Persistent& pcls);

2301 void AddClassTolndex(Class *cls);

2311 public:

2321 Parody(const string& name);

2331 -Parody(;

2341 static Parody *OpenDatabase()

2351 t return opendatabase; I

2361 1;

Page 260: C++ Database Development 1558283579

2371

2381

2391

2401

2411

242

243

2441

2451

2461

2471

2481

2491

2501

2511

2521

2531

2541

2551

2561

257

2581

2591

2601

2611

2621

2631

2641

2651

2661

2671

2681

2691

2701

271[

2721

2731

2741

Appendix C: Software Listings 255

// ---- Persistent constructor using last declared database

inline Persistent::Persistent() :parody(*Parody::OpenDatabase())

{

BuildObject();}

/ -- Persistent constructor using specified database

inline Persistent::Persistent(Parody& db) : parody(db)

t

BuildObject();I

template <class D>

void ReadObject(T& t)

{Persistent *oc - Persistent::ObjectBeingConstructedo;

oc->PdyReadObject(&t, sizeof(T));

I

template <class T>

void WriteObject(const T& t)

tPersistent *od = Persistent::ObjectBeingDestroyed(;

od->PdyWriteObject(&t, sizeof(T));

I

inline void ReadObject(string& s){

Persistent *oc = Persistent::ObjectBeingConstructedo;

oc->ReadStrObject(s):}

inline void WriteObject(const string& s)

Persistent *od = Persistent::ObjectBeingDestroyedo;

od->WriteStrObject(reinterpret-cast<const string&>(s));

Page 261: C++ Database Development 1558283579

256 c++ Database Development

2751J 1

276

2771 /---------------=-------------2781 / PersistentObject template

2791 /----------------------------2801 template <class T>

2811 class PersistentObject : public Persistent t

2821 void Read()

2831 {PdyReadObject(reinterpret cast<void*>(&Obj), sizeof(T));1

2841 void Write()

2851 {PdyWriteObject(reinterpret cast<void*>(&Obj), sizeof(T));}

2861 public:

2871 T Obj;

2881 PersistentObject(const T& obj) : Obj(obj)

2891 f LoadObject(); }

2901 PersistentObject(ObjAddr oa - 0)

2911 t LoadObject(oa); }

2921 virtual -PersistentObject()2931 t SaveObjecto); }

2941 };

2951

2961 /- ----------------

2971 / Reference template

2981 /-------------------------- ---

2991 template <class T>

3001 class Reference t

3011 public:

3021 T *obj;

3031 Referenceo;

3041 -Referenceo;

3051 void ReadObjecto;

3061 void WriteObjecto;3071 void operator-(T& to) throw (BadReference);

3081 void RemoveReferenceo);

3091 1;

3101

3111 template <class T>

3121 Reference<T>::Reference()

Page 262: C++ Database Development 1558283579

Appendix C: Software Listings 257

3131 {

3141 obj - 0;

3151 )3161

3171 template <class T>

3181 Reference<T>::-Reference()

3191 {3201 Persistent::Destroy(obj);

3211 13221

3231 template <class T>

3241 void Reference<T>::ReadObject()

3251 {

3261 Persistent::Destroy(obj);

3271 obj - 0;

3281

3291 ObjAddr oa;

3301 ::ReadObject(oa);

3311 if (oa 0= 0)

3321 obi = new T(oa);

3331

3341

3351 template <class T>

3361 void Reference<T>::WriteObject()

3371 {

3381 ObjAddr oa = 0;

3391 if (obi !- 0)3401 oa - obj->ObjectAddress);

3411 ::WriteObject(oa);

3421 1

34313441 template <class T>

3451 void Reference<T>::operator=(T& to)3461 throw (BadReference)

3471 f

3481 Persistent *po = dynamic cast<Persistent*>(&to);

3491 if (po -= 0)

3501 throw BadReferenceo;

Page 263: C++ Database Development 1558283579

258 C++ Database Development

Persistent::Destroy(obj);

obj - static-cast<T*>(po);obj->AddReference();

template <class T>

void Reference<T>::RemoveReference(

t

Persistent::Destroy(obj);

obj - 0;

3511

3521

35313541

3551

3561

3571

3581

35913601

3611

3621

3631

36413651

de "key.h"

LISTING C.10 PARODY.CPP

1 /---------- parody.c21

31 /41 / Parody persistent obj

51 II6j

718191

101111

121

131141

151

161171

181

19j

201

pp

ect member functions

#include <new.h>

#include <stdlib.h>

#include <string.h>

#include "parody.h"

Parody *Parody::opendatabase; // latest open database

I'

/1

'I

Parody member functions

// ----------- construct a Parody database

Parody::Parody(const string& name) :datafile(name), indexfile(name)

#inclu

#end if

Page 264: C++ Database Development 1558283579

Appendix C Software Listings 259

211 t

221 rebuildnode - 0;

231 previousdatabase - opendatabase;

241 opendatabase = this;

251 )

261

271 // ------- close the Parody database

281 Parody::-Parody()

291 f

301 PdyBtree *bt = btrees.FirstEntry);

311 while (bt != 0) {

321 delete bt;

33! bt = btrees.NextEntryo;

341

351 Class *cls =classes.FirstEntry();

361 while (cls 0= 0)

371 delete [] cls->classname;

381 delete cls;

391 cls = classes.NextEntryo;

401 1

411 opendatabase = previousdatabase;

421 1

431

441 // ------- read an object header record

451 void Parody::GetObjectHeader(ObjAddr ndObjectHeader &objhdr)

461 1

471 --- constructing this node seeks to the first data byte

481 Node(&datafile, nd);

491 datafile.ReadData(&objhdr. sizeof(ObjectHeader));

501 }

51!

521 Class *Parody::Registration(const Persistent& pcls)

531 (

541 Class *cls =classes.FirstEntry();

551 while (cls != D) {

561 const char *ty - typeid(pcls).name);

571 if (strcmp(cls->classname, ty) -= 0)

581 break;

Page 265: C++ Database Development 1558283579

260 c++ Database Development

591 cls = classes.NextEntry();

601

611 return cls;

621 1

63

641 bool Parody::FindClass(Class *cls, NodeNbr *nd)651 t

661 char classname[classnamesize];

671 ClasslD cid = 0;681 if (!indexfile.NewFileo) {

691 Node tmpnode;

701 NodeNbr nx = 1;

711 // ---- locate the class header

721 while (nx != 0) f

731 tmpnode = Node(&indexfile, nx):

741 indexfile.ReadData(classname, classnamesize);751 if (strcmp(classname, cls->classname) == 0) f

761 cls->headeraddr = indexfile.FilePosition();

771 cls->classid = cid;781 return true;

791

801 --- this node is not the class header

811 cid++;

821 nx = tmpnode.NextNode();

831

841 if (nd 0= 0) {

851 *nd= indexfile.NewNode();

861 tmpnode.SetNextNode(*nd);

871 }

881 1891 cls->classid = cid;

901 return false;

911 1

921

931 ClassID Parody::GetClassID(const char *classname)

941 f

951 Class cls(const cast<char*>(classname));

961 FindClass(&cls);

Page 266: C++ Database Development 1558283579

Appendix Software Listings 261

971 return cls.classid;

981 1991

100

1011 void Parody::AddClassTolndex(Class *cls)

1021 t

1031 NodeNbr nd = 0;

104

1051 if (FindClass(cls, &nd) == false) f

1061 indexfile.ResetNewFile(;

1071 nd = nd ? nd : indexfile.NewNode();

1081 // -- build the class header for new class

1091 Node tmpnode(&indexfile, nd);

1101

l111 // write class name into class record

1121 indexfile.WriteData(cls->classname, classnamesize);

1131

1141 -/ save disk address of tree headers

1151 cls->headeraddr = indexfile.FilePositiono ;

1161

1171 ----- pad the residual node space

1181 int residual = nodedatalength-classnamesize;

1191 char *residue = new char[residual];

1201 memset(residue, 0, residual);

1211 indexfile.WriteData(residue, residual);

1221 delete residue;

1231

1241 tmpnode.MarkNodeChangedo;

1251

1261 1

1271

1281 // register a class's indexes with the database manager

1291 void Parody::Registerlndexes(Class *cls, const Persistent& pcls)

1301 throw (ZeroLengthKey)

1311 1

1321 Persistent& cl - const cast<Persistent&>(pcls);

1331 PdyKey *key = cl.keys.FirstEntry();

1341 while (key 0= 0) t

Page 267: C++ Database Development 1558283579

262 C++ Database Development

1351 if (key->GetKeyLength() -- 0)1361 throw ZeroLengthKey();

1371 PdyBtree *bt = new PdyBtree(indexfile, cIs, key);

1381 bt->SetClasslndexed(cls);

1391 btrees.AppendEntry(bt);

1401 key - cl.keys.NextEntry();

1411 j

1421

1431

1441 /-/ - register a persistent class with the database manager

1451 ClassID Parody::RegisterClass(const Persistent& pcls)

1461 t

1471 Class *cIs = Registration(pcls);

1481 if (cls == 0) f

1491 cs = new Class;

1501 const char *cn = typeid(pcls).nameo;

1511 cls->classname - new char[strlen(cn)+1];

1521 strcpy(cls->classname, cn);

1531

1541 -- search the index file for the class

1551 AddClassTolndex(cls);

1561

1571 ---- register the indexes

1581 Registerlndexes(cls, pcls);

1591

1601 classes.AppendEntry(cls);

1611 }

1621 return cls->classid;

1631

1641

1651 //

166 // Persistent base class member functions

1671 //

1681

1691 Persistent *Persistent::objconstructed - 0;

1701 Persistent *Persistent::objdestroyed - 0;

1711 bool Persistent::usingnew = false;

1721

Page 268: C++ Database Development 1558283579

Appendix C Software Listings 263

1731 // --- common constructor code

1741 void Persistent::BuildObject() throw (NoDatabase)

175) {

1761 if (Parody::OpenDatabase() =-= 0)

1771 throw NoDatabase();

1781 prevconstructed = objconstructed;

1791 objconstructed = this;

1801 changed = false;

1811 deleted = false;

1821 newobject = false;

1831 loaded = false;

1841 saved - false;

1851 offset = 0;

1861 indexcount = 0;

1871 node = 0;

1881 objectaddress = 0;

189) instances = 0;

1901 1

1911

1921 // ----- destructor

1931 Persistent::-Persistent(

1941 throw (NotLoaded, NotSaved, MustDestroy)

1951

1961 if (Parody::OpenDatabase() -- 0)

1971 throw NoDatabaseo);

1981 RemoveObject();

1991 keys.ClearListo);

2001 delete node;

2011 if (!loaded)

2021 throw NotLoaded();

203) if (!saved)

204) throw NotSaved();

205) if (instances != 0)

206) throw MustDestroy();

207) 1

2081

209) void Persistent::Destroy(Persistent *pp)

2101 {

Page 269: C++ Database Development 1558283579

264 C++ Database Development

2111 if (pp != 0) t

2121 if (pp->instances -= 0)2131 delete(pp);

2141 else

2151 -- (pp->instances);

2161 121712181

2191 Persistent *Persistent::ObjectBeingConstructed()

2201 throw (NotlnConstructor)

2211 {

2221 Persistent *oc - objconstructed;

2231 if (oc -- 0)

2241 throw NotlnConstructoro:

2251 return oc;

2261

2271

2281 Persistent *Persistent::ObjectBeingfestroyed()2291 throw (NotlnDestructor)

2301 t

2311 Persistent *dc = objdestroyed;

2321 if (dc -= 0)

2331 throw NotlnDestructoro;2341 return dc;

2351

2361

2371 / -/ search the collected Btrees for this key's index2381 PdyBtree *Persistent::Findlndex(PdyKey *key)

2391 {

2401 PdyBtree *bt - 0;2411 if (key == 0)2421 key =keys.FirstEntry(;

2431 if (key 0) {

2441 bt = parody.btrees.FirstEntry();

2451 while (bt 1= 0) {

2461 const char *ty - typeid(*this).name();2471 if (strcmp(ty, bt->Classlndexed()->classname) =- 0)

2481 if (bt->Indexno() == key->indexno)

Page 270: C++ Database Development 1558283579

Appendix C Software Listings 265

2491 break;

2501 bt = parody.btrees.NextEntryo;

2511 }

2521 1

2531 return bt;

2541

2551

2561 ----- remove copies of the original keys

2571 void Persistent::RemoveOrgKeys()

2581

2591 PdyKey *ky = orgkeys.FirstEntryo;

2601 while (ky 0= 0) {

2611 delete ky;

2621 ky = orgkeys.NextEntryo;

2631

2641 orgkeys.ClearList();

2651 1

2661

2671 // record the object's state

2681 void Persistent::RecordObject()

2691 {

2701 --- remove object from the list of instantiated objects

2711 RemoveOrgKeys(;

2721 // --- remove copies of the original keys

2731 parody.objects.RemoveEntry(this);

2741 / --- put the object's address in a parody list of

2751 / instantiated objects

2761 parody.objects.AppendEntry(this);

2771 // ---- make copies of the original keys for later update

2781 PdyKey *key =keys.FirstEntry();

2791 while (key 0= 0) {

2801 PdyKey *ky = key->MakeKey();

2811 *ky = *key;

2821 orgkeys.AppendEntry(ky);

2831 // --- instantiate the index b-tree (if not already)

2841 Findlndex(ky);

2851 key = keys.NextEntry();

2861

Page 271: C++ Database Development 1558283579

266 C++ Database Development

2871

2881

2891 ---- remove the record of the object's state

2901 void Persistent::Remove0bjecto)

2911 t

2921 --- remove object from the list of instantiated objects

2931 parody.objects.RemoveEntry(this):

2941 // --- remove copies of the original keys

2951 RemoveOrgKeyso);

2961 }

2971

2981 void Persistent::TestDuplicateObject()

2991 throw (Persistent*)

3001 {

3011 if (objectaddress 1= 0) {

3021 // --- search for a previous instance of this object

3031 Persistent *obj = parody.objects.FirstEntry();

3041 while (obj != 0) {

3051 if (objectaddress -= obj->objectaddress) {

3061 // --- object already instantiated

3071 obj->instances++;

3081 saved = true;

3091 throw obj;

3101

3111 obj = parody.objects.NextEntry(;

3121

3131

3141

3151

3161 // called from derived constructor after all construction

3171 void Persistent::LoadObject(ObjAddr nd)

3181 {

3191 loaded - true;

3201 objconstructed = 0;

3211 objhdr.classid = parody.RegisterClass(*this);

3221 objectaddress = nd;

3231 if (parody.rebuildnode)

3241 objectaddress = parody.rebuildnode;

Page 272: C++ Database Development 1558283579

Appendix C Software Listings 267

3251 if (objectaddress == 0)

3261 // --- position at object's node

3271 Searchlndex(keys.FirstEntry());3281 ReadDataMembers();

3291 objconstructed = prevconstructed;

3301 }

3311

3321 // write the object to the database

3331 void Persistent::ObjectOut()

3341 {

3351 Persistent *hold = objdestroyed;

3361 objdestroyed = this;

3371 // --- tell object to write its data members

3381 Writeo;

3391 objdestroyed - hold;

3401 // --- pad the last node

3411 short int padding = nodedatalength - offset;

3421 if (padding)

3431 char *pads - new char[padding];

3441 memset(pads, 0, padding);

3451 parody.datafile.WriteData(pads, padding);3461 delete pads;

34713481 NodeNbr nx = node->NextNode();

3491 node->SetNextNode(O);

3501 delete node;

3511 node - 0;

3521 // --- if node was linked, object got shorter

3531 while (nx != 0) {

3541 Node nd(&parody.datafile, nx);3551 nx - nd.NextNode);

3561 nd.MarkNodeDeletedo;

3571

3581 parody.datafile.Seek(filepos);

35913601

3611 /------ write the object's node header

3621 void Persistent::WriteObjectHeader(

Page 273: C++ Database Development 1558283579

268 C++ Database Development

3631 f

3641 --- write the relative node number and class id

3651 parody.datafile.WriteData(&objhdr, sizeof(ObjectHeader));

3661 offset - sizeof(ObjectHeader);

3671 13681

3691 // ---- read the object's node header3701 void Persistent::ReadObjectHeader()

3711 (

3721 --- read the relative node number and class id

3731 parody.datafile.ReadData(&objhdr, sizeof(ObjectHeader));

3741 offset = sizeof(ObjectHeader);

3751 )

3761

3771 --- called from derived destructor before all destruction378! / a new or existing object is being saved

3791 void Persistent::Save0bject()3801 throw (NoDatabase)

381 {

382! if (Parody::OpenDatabase() -- 0)

3831 throw NoDatabase(;

3841 saved - true;

3851 if (parody.rebuildnode) t

3861 AddIndexeso;

3871 return;

3881

3891 if (newobject) t

3901 if (!deleted && ObjectExists()) t

3911 AddIndexeso;

3921 PositionNodeo;

393! ObjectOuto);

3941 RecordObject();

3951396! 1

3971 else if (deleted 11 changed && ObjectExists())3981 // --- position the parody file at the object's node

399! PositionNodeo;4001 if (deleted) {

Page 274: C++ Database Development 1558283579

Appendix C. Software listings 269

4011 --- delete the object's nodes from the database

4021 while (node != 0) f

4031 node->MarkNodeDeleted();

4041 NodeNbr nx = node->NextNode(;

4051 delete node;

4061 if (nx)

4071 node = new Node(&parody.datafile, nx);

4081 else

4091 node = 0;

4101

4111 Deletelndexes();

4121 objectaddress = 0;

4131 }

4141 else {

4151 // --- tell object to write its data members

4161 ObjectOut(;

4171 // update the object's indexes

4181 Updatelndexes(;

4191 RecordObjecto;

4201

4211 parody.datafile.Seek(filepos);

4221 1

4231 newobject = false;

4241 deleted = false;

4251 changed = false;

4261

4271

4281 // --- read one data member of the object from the database

4291 void Persistent::PdyReadObject(void *buf, short int length)

4301 f

4311 while (node != 0 && length > 0) f

4321 if (offset == nodedatalength) {

4331 NodeNbr nx = node->NextNodeo;

4341 delete node;

4351 node = nx ? new Node(&parody.datafile, nx) : 0;

4361 ReadObjectHeader();

4371

4381 if (node != 0) {

Page 275: C++ Database Development 1558283579

270 C++ Database Development

439j short int len - min(length,

4401 static cast<short>(nodedatalength-offset));

4411 parody.datafile.ReadData(buf, len);

4421 buf = reinterpret cast<char*>(buf) + len;

4431 offset += len;

4441 length -- len;

4451 }

4461 }

4471

44814491 // --- write one data member of the object to the database

4501 void Persistent::PdyWriteObject(const void *buf, short length)

4511 t

4521 while (node != 0 && length > 0) {

4531 if (offset == nodedatalength) f

4541 NodeNbr nx - node->NextNodeo;

4551 if (nx 0= 0)

4561 nx = parody.datafile.NewNode();

4571 node->SetNextNode(nx);

4581 delete node;

4591 node = new Node(&parody.datafile, nx);

4601 WriteObjectHeader();

4611 objhdr.ndnbr++;

4621 }

4631 short int len = min(length,

4641 static cast<short>(nodedatalength-offset));

4651 parody.datafile.WriteData(buf, len);

4661 buf - reinterpret-cast<const char*>(buf) + len;

4671 offset += len;

4681 length -= len;

4691

4701 }

4711

4721 void Persistent::ReadStrObject(string& str)

4731 {4741 short int len;

4751 PdyReadObject(&len, sizeof(short int));

4761 char *s = new char[len+1];

Page 276: C++ Database Development 1558283579

Appendix C Software Listings 271

4771 PdyReadObject(s, len);

4781 sElen] = '\0';

4791 str = s;4801 delete s;

4811 1

4821

4831 void Persistent::WriteStrObject(const string& str)

4841 t4851 short int len = strlen(str.c str());4861 PdyWriteObject(&len, sizeof(short int));

4871 PdyWriteObject(str.c-str(), len);

4881 1

4891

490 // ---- add the index values to the object's index btrees4911 void Persistent::Addlndexes()

4921

4931 PdyKey *key =keys.FirstEntry(;

4941 while (key 0) {

4951 if (!key->isNullValue )) {4961 PdyBtree *bt = Findlndex(key);

4971 key->fileaddr = objectaddress;

4981 bt->Insert(key);

49915001 key = keys.NextEntry();

5011

5021 J

50315041 // --- update the index values in the object's index btrees

5051 void Persistent::Updatelndexes()

50615071 PdyKey *oky = orgkeys.FirstEntryo;

5081 PdyKey *key = keys.FirstEntry();

5091 while (key !0 0) f

5101 if (!(*oky == *key)) {

5111 // --- key value has changed, update the index

5121 PdyBtree *bt = Findlndex(oky);5131 // --- delete the old

5141 if (!oky->isNullValue())

Page 277: C++ Database Development 1558283579

272 C++ Database Development

5151 oky->fileaddr = objectaddress;

5161 bt->Delete(oky);

5171 }

5181 --- insert the new

5191 if (!key->isNullValueC)) {

5201 key->fileaddr = objectaddress;

5211 bt->Insert(key);

5221 1

5231

5241 oky = orgkeys.NextEntry();

5251 key = keys.NextEntry(;

5261

5271

5281

5291 / -- delete the index values from the object's index btrees

5301 void Persistent::Deletelndexes()

5311 t

5321 PdyKey *key = orgkeys.FirstEntryo;

5331 while (key 0= 0) {

5341 if (!key->isNullValueo) f

5351 PdyBtree *bt = Findlndex(key);

5361 key->fileaddr = objectaddress;

5371 bt->Delete(key);

5381 1

5391 key = orgkeys.NextEntry();

54015411

542

5431 ----- position the file to the specifed node number

5441 void Persistent::PositionNode()

5451 throw (BadObjAddr)

5461 f

5471 filepos - parody.datafile.FilePositiono;

5481 if (objectaddress) t

5491 delete node;

5501 node = new Node(&parody.datafile, objectaddress);

5511 offset = sizeof(ObjectHeader);

5521 ObjectHeader oh;

Page 278: C++ Database Development 1558283579

Appendix C Software Listings 273

5531 parody.datafile.ReadData(&oh, sizeof(ObjectHeader));

5541 if (oh.ndnbr != 0 11 oh.classid != objhdr.classid)

5551 throw BadObjAddr(;

5561

5571 J

5581

5591 // ------- search the index for a match on the key

5601 void Persistent::Searchlndex(PdyKey *key)

5611 {

5621 objectaddress - 0;

5631 if (key !- 0 && !key->isNullValue()

5641 PdyBtree *bt - Findlndex(key);

5651 if (bt !- 0 && bt->Find(key))

5661 if (key->indexno !0 0) t

5671 PdyKey *bc;

5681 do

5691 bc - bt->Previous();

5701 while (bc !- 0 && *bc == *key);

5711 key - bt->Next();

5721

5731 objectaddress = key->fileaddr;

5741

5751

5761 1

5771

5781 // --- scan nodes forward to the first one of next object

5791 void Persistent::ScanForward(NodeNbr nd)

5801 {

5811 ObjectHeader oh;

5821 while (nd++ < parody.datafile.IHighestNode()) {

5831 parody.GetObjectHeader(nd, oh);

5841 if (oh.classid == objhdr.classid && oh.ndnbr == 0)

5851 objectaddress - nd;

5861 break;

5871 1

5881

5891

5901

Page 279: C++ Database Development 1558283579

274 C++ Database Development

5911 // --- scan nodes back to first one of the previous object

5921 void Persistent::ScanBackward(NodeNbr nd)

5931 {

5941 ObjectHeader oh;

5951 while (--nd > 0) {5961 parody.GetObjectHeader(nd, oh);

5971 if (oh.classid == objhdr.classid && oh.ndnbr == 0)

5981 objectaddress = nd;

5991 break;

6001 }

601 }

6021 1

603

6041 // ---------- find an object by a key value

6051 Persistent& Persistent::FindObject(PdyKey *key)

6061 t

6071 RemoveObject();

6081 Searchlndex(key);

6091 ReadDataMemberso;

6101 return *this;

6111 }

6121

6131 --- retrieve the current object in a key sequence

6141 Persistent& Persistent::CurrentObject(PdyKey *key)

6151 f6161 RemoveObject(;

6171 PdyBtree *bt = Findlndex(key);

6181 if (bt != 0) t

6191 if ((key = bt->Currento) != 0)6201 objectaddress = key->fileaddr;

6211 ReadDataMembers();

6221 1

6231 return *this;

6241 J

6251

6261 // --- retrieve the first object in a key sequence

6271 Persistent& Persistent::FirstObject(PdyKey *key)

6281 f

Page 280: C++ Database Development 1558283579

Appendix kC Software Listings 275

6291 RemoveObject(;

6301 objectaddress = 0;

6311 PdyBtree *bt = Findlndex(key);6321 if (bt -- 0)

6331 // ---- keyless object

6341 ScanForward(O);

6351 else if ((key - bt->Firsto) !- 0)

6361 objectaddress - key->fileaddr;

6371 ReadDataMembers);6381 return *this;

6391 1

6401

6411 // --- retrieve the last object in a key sequence

6421 Persistent& Persistent::LastObject(PdyKey *key)

6431 {

6441 RemoveObject()

6451 objectaddress - 0;

6461 PdyBtree *bt = Findlndex(key);

6471 if (bt -- 0)

6481 // -- keyless object

6491 ScanBackward(parody.datafile.HighestNode());

6501 else if ((key - bt->Last()) !- 0)

6511 objectaddress - key->fileaddr;

6521 ReadDataMemberso;

6531 return *this;

6541 }

6551

6561 // --- retrieve the next object in a key sequence

6571 Persistent& Persistent::NextObject(PdyKey *key)6581 t

6591 RemoveObject();

6601 ObjAddr oa - objectaddress;

6611 objectaddress 0 0;

6621 PdyBtree *bt = Findlndex(key);

6631 if (bt -- 0)

6641 // ------ keyless object

6651 ScanForward(oa);6661 else if ((key - bt->Next()) !- 0)

Page 281: C++ Database Development 1558283579

276 C++ Database Development

6671 objectaddress = key->fileaddr;

6681 ReadDataMembers();

6691 return *this;

6701 1

671

6721 // --- retrieve the previous object in a key sequence

6731 Persistent& Persistent::PreviousObject(PdyKey *key)

6741 {

6751 RemoveObjecto;

6761 ObjAddr oa = objectaddress;

6771 objectaddress = 0;

6781 PdyBtree *bt = Findlndex(key):

6791 if (bt == 0)

6801 // keyless object

6811 ScanBackward(oa);

6821 else if ((key = bt->Previous)) != 0)

6831 objectaddress = key->fileaddr;

6841 ReadDataMembers();

6851 return *this;

6861

6871

6881 ------- read an object's data members

6891 void Persistent::ReadDataMembers()

6901

6911 if (objectaddress 0= 0) t

6921 PositionNodeo);

6931 // --- tell object to read its data members

6941 Persistent *hold = objconstructed;

6951 objconstructed = this;

6961 Read();

6971 objconstructed = hold;

6981 delete node;

6991 node = 0:

7001 TestDuplicateObjecto;

7011 / --- post object instantiated and

7021 / put secondary keys in table

7031 RecordObjecto;

7041 parody.datafile.Seek(filepos);

Page 282: C++ Database Development 1558283579

Appendix : Software Listings 277

7051

7061

7071

7081 -------- add an object to the Parody database

7091 boo] Persistent::AddObject()

7101 t7111 newobject = (objectaddress == 0 && TestRelationshipso);

7121 if (newobject) {

7131 delete node; // (just in case)7141 node - new Node(&parody.datafile,

7151 parody.datafile.NewNode());7161 objectaddress = node->GetNodeNbro;

7171 WriteObjectHeadero;

7181 objhdr.ndnbr++;

7191 1

7201 return newobject;

7211

7221

7231 ---------- mark a persistent object for change

7241 bool Persistent::ChangeObject(

7251

7261 changed = TestRelationshipso;

7271 return changed;

7281 1

7291

7301 // - mark a persistent object for delete7311 bool Persistent::Delete0bject()

7321 {

7331 PdyKey *key -keys.FirstEntryo);

7341 bool related = false;

7351

7361 if (!key->isNullValue())

7371 // --- scan for other objects related to this one7381 PdyBtree *bt = parody.btrees.FirstEntryo;

7391 while (bt != 0 && !related) f

740 // --- test only secondary keys

7411 if (bt->Indexno() 0= 0)

7421 const Type info *relclass =

Page 283: C++ Database Development 1558283579

278 C++ Database Development

7431 bt->NullKey()->relatedclass;

7441 if (relclass ! 0 0) {

7451 if (typeid(*this) -- *relclass) {

7461 PdyKey *ky - bt->MakeKeyBuffero;

7471 if (ky->isObjectAddresso) t

7481 const ObjAddr *oa -

7491 ky->ObjectAddress(;

7501 ObjectHeader oh;

7511 parody.GetObjectHeader(*oa, oh);

7521 if (oh.classid -- objhdr.classid)

7531 if (oh.ndnbr -- 0)

7541 related - true;

7551

7561 else t

7571 ky->CopyKeyData(key);

7581 related - bt->Find(ky);

7591

7601

7611

7621

7631 bt = parody.btrees.NextEntryo;

7641

7651

7661 deleted - !related;

7671 return deleted;

7681 1

7691

7701 ------ test an object's relationships

7711 // return false if it is related to a

7721 // nonexistent object

7731 // return false if its primary key is already in use

7741 bool Persistent::TestRelationships()

7751 t

7761 PdyKey *key - keys.FirstEntry();

7771 if (key -- 0)

7781 return true;

7791 PdyBtree *bt;

7801 if (objectaddress -- 0) t

Page 284: C++ Database Development 1558283579

Appendix C Software Listings 279

7811 bt = Findlndex(key);

7821 if (bt != 0 && bt->Find(key))

7831 return false;

7841

7851 bool unrelated = true;

7861

7871 while ((key = keys.NextEntry)) != 0) 1

7881 const Type-info *relclass = key->relatedclass;

7891 if (key->isObjectAddress())

7901 const ObjAddr *oa = key->ObjectAddresso;

7911 if (oa != 0) 1

7921 ObjectHeader oh;

7931 parody.GetObjectHeader(*oa, oh);

7941 if (oh.ndnbr == 0) 1

795 // --- find classid of related class

7961 Class *cls = parody.classes.FirstEntry();

7971 const char *cn = relclass->name();

7981 while (cls != 0) 1

7991 if (strcmp(cn, cls->classname)==O)

8001 break;

8011 cls = parody.classes.NextEntry();

8021

8031 if (cls && cls->classid == oh.classid)

8041 continue;

8051

8061 unrelated = false:

8071

8081

8091 else if (!key->isNullValue() && relclass 0)

8101 const char *kc = relclass->name);

8111 bt = parody.btrees.FirstEntry);

8121 while (bt != 0 && unrelated) t

8131 // --- test only primary keys

8141 if (bt->Indexno() == 0) t

8151 // --- primary key of related class?

8161 const char *bc =

8171 bt->Classlndexed()->classname;

8181 if (strcmp(bc, kc) -= 0) {

Page 285: C++ Database Development 1558283579

PdyKey *ky = bt->MakeKeyBuffero;

ky->CopyKeyData(key);

unrelated = bt->Find(ky);}

bt = parody.btrees.NextEntryo;

return unrelated;

11 // ------------- key.h

21

31 #ifndef KEY H

41 #define KEY H

51I-I-

IIPdyKey abstract base class

class PdyKey

friend class

friend class

friend class

friend class

NodeNbrNodeNbr

virtual

virtualvirtual

virtualvirtual

vi rtual

virtualprotected:

Parody;

PdyBtree;

TNode;

Persistent;

fileaddr; II object address -> by this keylowernode; // lower node of keys > this key

void WriteKey(IndexFile& bfile) = 0;

void ReadKey(IndexFile& bfile) = 0;

bool isNullValue() const = 0;

void CopyKeyData(const PdyKey *key) - 0;

bool isObjectAddress() const - 0;

const ObjAddr *ObjectAddress() const = 0;

PdyKey *MakeKey() const - 0;

241 const Type-info *relatedclass; // related class

280 c++ Database Development

8191

8201

821

8221

8231

8241

8251

8261

8271

8281

8291 if

LISTING C.1 1 KEY.H

61718191

101

111121

131

141

151161

171181191

201

211

221

231

Page 286: C++ Database Development 1558283579

Appendix C Software Listings 281

251 IndexNo indexno; // O=primary key, >0 =secondary key

261 KeyLength keylength; // length of the key

271 public:

281 PdyKey(NodeNbr fa = 0);

291 virtual -PdyKey()

301 { /* ... */ 1

311 virtual int operator>(const PdyKey& key) const 0;

321 virtual int operator==(const PdyKey& key) const = 0;

331 virtual PdyKey& operator=(const PdyKey& key);

341 void Relate(const Type-info *ti)

351 { relatedclass = ti; }

361 KeyLength GetKeyLength() const

371 t return keylength; J

381 void SetKeyLength(KeyLength kylen)

391 { keylength = kylen; }

401 J;411

421 /

431 / Key class

44 //

451 template <class T>

461 class Key : public PdyKey t

471 T ky;

481 bool isObjectAddress() const

491 t return typeid(T) -= typeid(ObjAddr); }

501 const ObjAddr *ObjectAddress() const

511 t return reinterpret cast<const ObjAddr*>(&ky); 1

521 void CopyKeyData(const PdyKey *key);

531 PdyKey *MakeKeyo) const;

541 public:

551 Key(const T& key);

561 virtual -Key()

571 ( /* . */

581 PdyKey& operator=(const PdyKey& key);

59 int operator>(const PdyKey& key) const;

601 int operator==(const PdyKey& key) const;

611 T& KeyValue()

621 t return ky; I

Page 287: C++ Database Development 1558283579

282 C++ Database Development

631 void SetKeyValue(const T& key)641 f ky = key; }651 const T& KeyValue() const

661 f return ky; }671 virtual void WriteKey(IndexFile& ndx);

681 virtual void ReadKey(IndexFile& ndx);

691 bool isNullValue() const;

701 1;

711

721 template <class T>

731 Key<T>::Key(const T& key) : ky(key)

741 {

751 keylength = sizeof(T);

761 1

771

781 template <class T>

791 void Key<T>::CopyKeyData(const PdyKey *key)801 {

811 const Key<T> *kp =

821 static cast<const Key<T>*>(key);

831 ky = kp->ky;

841 }

851

861 template <class T>

871 PdyKey& Key<T>::operator=(const PdyKey& key)881 f

891 if (this != &key) t

901 PdyKey::operator=(key);

911 CopyKeyData(&key);

921 1

931 return *this;

941 1

951

961 template <class T>

971 int Key<T>::operator>(const PdyKey& key) const981 t

991 const Key<T> *kp =

1001 static cast<const Key<T>*>(&key);

Page 288: C++ Database Development 1558283579

Appendix Software Listings 283

101 return ky > kp->ky;

1021 1

1031

1041 template <class T>1051 int Key<T>::operator--(const PdyKey& key) const

1061 {1071 const Key<T> *kp -

1081 static cast<const Key<T>*>(&key);

1091 return ky -- kp->ky;

1101

liII1121 template <class T>

1131 PdyKey *Key<T>::MakeKey() const1141

1151 PdyKey *newkey - new Key<T>(T(O));

1161 newkey->SetKeyLength(keylength);

1171 return newkey;1181 )

1191

1201 // --- ReadKey must be specialized if key I= simple data type1211 template <class T>

1221 void Key<T>::ReadKey(IndexFile& ndx)

1231 {

1241 if (keylength > 0)

1251 ndx.ReadData(&ky, keylength);

1261 1

1271

1281 // --- WriteKey must be specialized if key I- simple data type

1291 template <class T>

1301 void Key<T>::WriteKey(IndexFile& ndx)

1311

1321 if (keylength > 0)

1331 ndx.WriteData(&ky, keylength);

1341 11351

1361 template <class T>

1371 bool Key<T>::isNullValue() const

1381 {

Page 289: C++ Database Development 1558283579

284 C++ Database Development

return ky =- T(O);

11

I.I.I,

/

/ specialized Key<string> template member functions

1391

1401

1411

1421

1431

1441

1451

1461

1471

1481

1491

1501

1511

1521

1531

154

1551

1561

1571

1581

15911601

1611

1621

1631

1641

1651

1661

1671

1681

1691

1701

1711

1721

1731

1741

1751

1761

inline Key<string>::Key(const string& key) ky(key)

{

keylength = key.length(;

I

inline void Key<string>::ReadKey(IndexFile& ndx)

{char *cp = new char[keylength+1]:

ndx.ReadData(cp, keylength);*(cp+keylength) = '\0';

ky = string(cp);

delete cp;}

inline void Key<string>::WriteKey(IndexFile& ndx)

t

ky.resize(keylength);

ndx.WriteData(ky.c-str(),keylength);

I

inline PdyKey *Key<string>::MakeKey() constI

PdyKey *newkey =

new Key<string>(string('\O',keylength));

newkey->SetKeyLength(keylength);

return newkey;}

inline bool Key<string>::isNullValue() const

{return ky.is null();

Page 290: C++ Database Development 1558283579

Appendix C: Software Listings 285

1771781 /1791 / Concatenated key class

1801 /

1811 template <class TI, class T2>1821 class CatKey : public PdyKey {

1831 Key<TI> kyl;1841 Key<T2> ky2;

1851 bool isObjectAddress() const

1861 { return false; }

1871 const ObjAddr *ObjectAddress() const

1881 t return 0; 1

1891 void CopyKeyData(const PdyKey *key);1901 --- ReadKey/WriteKey must be specialized1911 / if key(s) != simple data types

1921 virtual void ReadKey(IndexFile& ndx)

1931 kyl.ReadKey(ndx); ky2.ReadKey(ndx); }

1941 virtual void WriteKey(IndexFile& ndx)1951 { kyl.WriteKey(ndx); ky2.WriteKey(ndx); }

1961 PdyKey *MakeKey() const;

1971 bool isNullValue() const1981 f return kyl.isNullValue() && ky2.isNullValue(); 1

1991 public:2001 CatKey(const T1& keyl, const T2& key2);

2011 -CatKey() {H

2021 PdyKey& operator=(const PdyKey& key);

2031 int operator>(const PdyKey& key) const;

2041 int operator==(const PdyKey& key) const;

2051 Key<T1>& Key1()

2061 { return kyl; }2071 TI& KeyValuel()

2081 { return kyl.KeyValue(; }2091 const T1& KeyValuel() const2101 { return kyl.KeyValueo; }

2111 void SetKeyValuel(const TI& keyl)2121 f kyl.SetKeyValue(keyl); I

2131 Key<T2>& Key2()

2141 f return ky2; J

Page 291: C++ Database Development 1558283579

286 c++ Database Development

2151 T2& KeyValue2()

2161 t return ky2.KeyValue(); 1

2171 const T2& KeyValue2() const

2181 ( return ky2.KeyValueo; }2191 void SetKeyValue2(const T2& key2)

2201 t ky2.SetKeyValue(key2); 1

2211 1:

2221

2231 template <class T1, class T2>

2241 CatKey<T1,T2>::CatKey(const TI& keyl, const T2& key2)

2251 kyl(keyl),ky2(key2)

2261 t

2271 keylength - kyl.GetKeyLength() + ky2.GetKeyLengthO;

2281 )2291

2301

2311 template <class Ti, class T2>

2321 int CatKey<T1,T2>::operator>(const PdyKey& key) const

2331 (

2341 const CatKey<T1,T2> *ckp -

2351 static cast<const CatKey<T1,T2>*>(&key);

2361 if (kyl > ckp->kyl)

2371 return 1;

2381 if (kyl -- ckp->kyl && ky2 > ckp->ky2)

2391 return 1;

2401 return 0;

2411 }

2421

2431 template <class TI. class T2>

2441 int CatKey<TI,T2>::operator--(const PdyKey& key) const

2451 {

2461 const CatKey<T1,T2> *ckp -2471 static cast<const CatKey<T1,T2>*>(&key);

2481 return kyl -- ckp->kyl && ky2 -- ckp->ky2;2491 }

2501

2511 template <class Ti, class T2>2521 void CatKey<T1,T2>::CopyKeyData(const PdyKey *key)

Page 292: C++ Database Development 1558283579

2531

2541

2551

2561257!

2581

2591

2601

2611

2621

2631

2641

2651

2661

267

2681

2691

2701

2711

2721

2731

2741

2751

2761

2771

2781

2791

2801

LISTING C.12 KEY.CPP

1 // -------------- key.cpp2131 #include "parody.h"41

51 /61 / base PdyKey class member functions

7 //

Appendix C: Software Listings 287

const CatKey<T1,T2> *ckp =

static cast<const CatKey<T1,T2>*>(key);

kyl - ckp-Akyl;ky2 - ckp->ky2;

template <class T1, class T2>

PdyKey& CatKey<Ti,T2>::operator=(const PdyKey& key)

{if (this != &key) {

PdyKey::operator-(key);

CopyKeyData(&key);

return *this;

template <class TI, class T2>

PdyKey *CatKey<TI,T2>::MakeKey() const

CatKey<TI,T2> *newkey = new CatKey<T1,T2>(T1(0),T2(0));

newkey->kyl.SetKeyLength(kyl.GetKeyLength();

newkey->ky2.SetKeyLength(ky2.GetKeyLength());

newkey->SetKeyLength(keylength);

return newkey;

i

#endi f

Page 293: C++ Database Development 1558283579

288 C++ Database Development

81

91 PdyKey::PdyKey(NodeNbr fa)101 {

1il fileaddr - fa;

121 lowernode = 0;

131 indexno = 0;

141 relatedclass = 0;

151 if (Persistent::objconstructed != 0) {

161 // --- register the key with the object being built

171 Persistent::objconstructed->RegisterKey(this);

181 // --- assign index number based on position in object191 indexno = Persistent::objconstructed->indexcount++;

201 }

211 }

221

231 //---- overloaded -

241 PdyKey& PdyKey::operator-(const PdyKey& key)

251 {

261 if (this !- &key) f

271 fileaddr = key.fileaddr;

281 lowernode - key.lowernode;291 indexno - key.indexno;

301 keylength - key.keylength;

311 relatedclass - key.relatedclass;

321

331 return *this;

341 1

LISTING C.13 BTREE.H

11 // ------------- btree.h

21

31 #ifndef BTREEH

41 #define BTREE H

5161 #include <fstream.h>

71 #include <string.h>

81 #include "linklist.h"

Page 294: C++ Database Development 1558283579

#include "node.h"

class PdyKey;

class TNode;

class Class;

const int classnamesize = 32;

91

101

ill

121

131141

151161

171

181

191201

211

221

23124

251

261271

281291

301311

321

331

IndexFile class

class IndexFile : public NodeFile

public:IndexFile(const string& name)

/ /* ... */ }};

N

:NodeFilIe (name+". ndx")

// -- b-tree header record

class TreeHeader t

friend class PdyBtree;

friend class IndexFile;

NodeNbr rootnode; // node number of the root

KeyLength keylength; // length of a key in this b-tree

TreeHeader()

f rootnode = 0; keylength = 0; 1

341 ;

351

361 /-- ---- b-tree index

371 class PdyBtree f

381 TreeHeader header;

391 TNode *trnode;

401 PdyKey *nullkey;

411 IndexFile& index;

421 IndexNo indexno;

431 Class *classindexed;

441 NodeNbr currnode;

451 NodeNbr oldcurrnode;

461 short oldcurrkey;

I-II

I-

I-

I-

I-

I-

II

II

btree header

-> current node value

for building empty derived key

index file this tree lives in

O=primary key, > O=secondary key

-> class structure of indexed class

current node number

for repositioning

Appendix C: Software Listings 289

I-

I-

II

Page 295: C++ Database Development 1558283579

290 c++ Database Development

471 streampos HdrPoso)

481 { return classindexed->headeraddr +

491 indexno * sizeof(TreeHeader); }

501 void ReadHeader()

511 { index.ReadData(&header,sizeof(TreeHeader),HdrPos());}

521 void WriteHeader()

531 { index.WriteData(&headersizeof(TreeHeader),HdrPos());}

541 void SaveKeyPosition();

551 public:

561 PdyBtree(IndexFile& ndx, Class *cls, PdyKey *ky)

571 throw (BadKeylength);

581 -PdyBtree(;

591 void Insert(PdyKey *keypointer);

601 void Delete(PdyKey *keypointer);

611 bool Find(PdyKey *keypointer);

621 PdyKey *Current();

631 PdyKey *First();

641 PdyKey *Last();

651 PdyKey *Next();

661 PdyKey *Previous();

671 IndexFile& GetlndexFile() const

681 { return index; }

691 PdyKey *NullKey() const

701 { return nullkey; }

711 PdyKey *MakeKeyBuffer() const;

721 NodeNbr Root() const

731 { return header.rootnode; }

741 NodeNbr GetKeyLengtho) const

751 { return header.keylength; 1

761 IndexNo Indexno() const

771 f return indexno; I

781 const Class *Classlndexed() const

791 f return classindexed; }

801 void SetClasslndexed(Class *cid)

811 f classindexed = cid; }

821 };

831

841 // ------------- b-tree TNode class

Page 296: C++ Database Development 1558283579

Appendix C: Software Listings 291

class TNode : Node t

friend class PdyBtree;

struct TNodeHeader {

boo] isleaf;

NodeNbr parent;

NodeNbr leftsibling;

NodeNbr rightsiblingshort int keycount;

NodeNbr lowernode;

IIII

II

II

I-

I-

II

true if node is a leaf

parent to this node

left sibling node

right sibling node

number of keys in this node

lower node associated with

keys < keys in this node

851861

871881891

90!

911921

931

941

95196!

971

981

9911001

101l1021

1031

1041

1051

1061

10711081

1091

1101

1111

1121

1131

1141

1151

1161

1171

1181

1191

1201

12111221

J header;PdyKey *currkey;

PdyBtree *btree;

LinkedList<PdyKey>

I-I-

IIkeys;

current key

btree that owns this node

the keys in this node

TNode(PdyBtree *bt, NodeNbr node);

bool SearchNode(PdyKey *keyvalue);

void Insert(PdyKey *keyvalue);short int mo;

void WriteBtreeKey(PdyKey *thiskey);

void Adopt(NodeNbr node);

void Adoption(;

bool isLeaf() constf return header.isleaf; I

NodeNbr Parent() const

{ return header.parent; I

NodeNbr LeftSibling() const

{ return header.leftsibling; I

NodeNbr RightSibling() constt return header.rightsibling; I

short int KeyCount() const

t return header.keycount; I

NodeNbr LowerNodeo) const

t return header.lowernode; Ibool Redistribute(NodeNbr sib);

TNodeHeader()

isleaf = false; parent = leftsibling =

rightsibling = keycount = lowernode = 0; 1

Page 297: C++ Database Development 1558283579

292 c++ Database Development

1231 boo] Implode(TNode& right);

1241 short int NodeHeaderSizeo) const

1251 return sizeof(TNodeHeader)+Node::NodeHeaderSize(); }

1261 TNode& operator=(TNode& tnode);1271 public: // due to a bug in Borland C++ 4.0

1281 -TNodeo;1291 1;

1301

1311 #endif

LISTING C.14 BTREE.CPP

1 // ----------------- btree.cpp

2131 #include <string.h>

41 #include "parody.h"

617181

91101

11i

121

131

141

151

161

171

181

191201

211

221

231

241

251

261

// ---------- constructor to open a btree

PdyBtree::PdyBtree(IndexFile& ndx, Class *cls, PdyKey *ky)

throw (BadKeylength)

index(ndx)

nullkey = ky->MakeKeyo);

nullkey->PdyKey::operator=(*ky);

trnode = 0;

classindexed = cls;

currnode = 0;

indexno = ky->indexno;

// -- read the btree header

ReadHeader();

if (header.keylength == 0)

header.keylength = ky->keylength;

else if (ky->keylength !- 0 &&

header.keylength != ky->keylength)

throw BadKeylengtho;

Page 298: C++ Database Development 1558283579

Appendix C Software Listings 293

271 1

281

291 // --- -- destructor for a btree

301 PdyBtree::-PdyBtree()

311 {

321 // write the btree header

331 WriteHeader(;

341 delete trnode;

351 delete nullkey;

361

371

381 -- - - make a key buffer

391 PdyKey *PdyBtree::MakeKeyBuffer() const

401 {

411 PdyKey *thiskey - nullkey->MakeKey(;

421 thiskey->indexno = indexno;

431 return thiskey;

441 J

451

461 // insert a key into a btree

471 void PdyBtree::Insert(PdyKey *keypointer)

48 {

491 - -- don't insert duplicate keys

501 if (!Find(keypointer)) {

511

521 PdyKey *newkey = keypointer->MakeKey(;

531 *newkey = *keypointer;541

551 NodeNbr rootnode = 0, leftnode = 0, rightnode = 0;

561 bool RootisLeaf - true;

571

581 bool done = false;

591 // --------- insert key into btree

601 while (currnode) {

611 int em = trnode->mo;

621 trnode->Insert(newkey);

631 / first insertion is into leaf

641 / if split, later insertions

Page 299: C++ Database Development 1558283579

// are into parents (non-leaves)

if (!trnode->header.isleaf)

trnode->currkey->lowernode = rightnode;

done - trnode->header.keycount <- em;

if (Odone)

---- node is full,// try to redistribute keys among siblings

done - trnode->Redistribute(

651

661

671681

691701

711721

731741

751761

771781

791

801811

821

831

841

851

861

871

881891

901911921

931

941

951961

971981

9911001

101l

1021

trnode->header.leftsibling);

trnode->Redistribute(

trnode->header.rightsibling);

if (done)

break;

/--- cannot redistribute fil

RootisLeaf = false;

led node, split it

rightnode =index.NewNode();

leftnode = currnode;

TNode right(this, rightnode);

right. SetNodeNbr(rightnode);

right.MarkNodeChanged();

S--- establish sibling and parent relationships

// between current node and new right sibling

right.header.rightsibling =

trnode->header.rightsibling;

trnode->header.rightsibling - rightnode;

right.header.leftsibling - currnode;

right.header.parent - trnode->header.parent;

/-/ -- if the current node is a leaf,

// so is the new sibling

right.header.isleaf = trnode->header.isleaf;

294 C++ Database Development

if (!done)

done =

Page 300: C++ Database Development 1558283579

Appendix C Software Listings 295

1031 // compute new key counts for the two nodes

1041 trnode->header.keycount = (em + 1) / 2;

1051 right.header.keycount = em-trnode->header.keycount;

1061

1071 // locate the middle key in the current node

1081 PdyKey *middlekey =

1091 trnode->keys.FindEntry(trnode->header.keycount);

110

111 // set the pointer to keys less than

1121 / those in new node

1131 if (!right.header.isleaf)

1141 right.header.lowernode = middlekey->lowernode;

1151

116! ---- point to the keys to move (1 past middle)

1171 PdyKey *movekey = trnode->keys.NextEntry(middlekey);

1181

1191 // -- middle key inserts into parent

1201 trnode->keys.RemoveEntry(middlekey);

1211 *newkey = *middlekey;

1221 delete middlekey;

1231

1241 /--- move keys from current to new right node

1251 for (int i = 0; i < right.header.keycount; i++)

1261 PdyKey *nkey = trnode->keys.NextEntry(movekey);

1271 trnode->keys.RemoveEntry(movekey);

1281 right.keys.AppendEntry(movekey);

1291 movekey = nkey;

1301

1311

1321 // prepare to insert key

1331 // into parent of split nodes

1341 currnode = trnode->header.parent;

1351 if (!currnode) f

1361 // ---- no parent node, splitting the root node

1371 rootnode = index.NewNode();

1381 right.header.parent = rootnode;

1391 trnode->header.parent = rootnode;

1401

Page 301: C++ Database Development 1558283579

296 C++ Database Development

1411

1421 --- the former right sibling of the current node

1431 / is now the right sibling of the split node

1441 / and must record the new node as left sibling

1451

1461 if (right.header.rightsibling) {

1471 TNode farright(this, right.header.rightsibling);

1481 farright.header.leftsibling = rightnode;

1491 farright.MarkNodeChangedo;

1501

1511

1521 / --- children of the new split node point to

1531 / the current split node as parent. They must

1541 // be adopted by the new split node

1551

1561 if (!right.header.isleaf)

1571 right.Adoptiono;

1581

1591 ----- if splitting other than root, read parent

1601 / position currkey to key where split node

1611 / key will be inserted

162

1631 if (currnode) {

1641 delete trnode; // writes the split node to disk

1651 // --- get the parent of the split nodes

1661 trnode = new TNode(this, currnode);

1671 // -- position currkey where new key will insert

1681 trnode->SearchNode(newkey):

1691 }

1701

1711

1721 if (!done) {

1731 // -- new root node ---- */

1741 delete trnode;

1751 if (rootnode == 0)

1761 rootnode =index.NewNodeo;

1771 trnode = new TNode(this, rootnode);

1781 trnode->header.isleaf = RootisLeaf;

Page 302: C++ Database Development 1558283579

Appendix C: Software Listings 297

currnode = header.rootnode = rootnode;

trnode->SetNodeNbr(rootnode);

trnode->Insert(newkey);

trnode->header.parent = 0;

trnode->header.keycount = 1;

if (!RootisLeaf) t

trnode->header.lowernode = leftnode;

trnode->currkey->lowernode = rightnode;

trnode->MarkNodeChanged()

delete newkey;

1791

180

181!

1821

1831

1841

1851

1861

1871

1881

189!

190

19111921

1931

1941

1951

196!

1971

1981

1991

2001

2011

202!

2031

2041

2051

206!

2071

2081

2091

210!

2111

21212131

2141

215!

216!

I

//bool PdyBtree

------- find a key in a btree

::Find(PdyKey *keypointer)

oldcurrnode = 0;

oldcurrkey = 0;

currnode = header.rootnode;

while (currnode)

delete trnode;

trnode = 0;

void PdyBtree::SaveKeyPosition({

if (trnode->header.isleaf) t

oldcurrnode = 0;

oldcurrkey = 0;}

else {

oldcurrnode = currnode;

oldcurrkey = trnode->keys.FindEntry(trnode->currkey);

I

t

Page 303: C++ Database Development 1558283579

298 c++ Database Development

2171 delete trnode;

2181 trnode = new TNode(this, currnode);

2191

2201 if (trnode->SearchNode(keypointer)) {

2211 // --- search key is equal to a key in the node

2221 keypointer->fileaddr = trnode->currkey->fileaddr;

2231 oldcurrnode = 0;2241 oldcurrkey = 0;2251 return true;

2261

2271

2281 if (trnode->currkey - trnode->keys.FirstEntryo) 1

2291 // --- search key is < lowest key in node

2301 SaveKeyPosition();

2311 if (trnode->header.isleaf)

2321 break;

2331 currnode = trnode->header.lowernode;

2341

2351 else if (trnode->currkey) {

2361 // --- search key is < current key in node

2371 SaveKeyPosition();

2381 if (trnode->header.isleaf)

2391 break;

2401 currnode =

2411 trnode->keys.PrevEntry(trnode->currkey)->

2421 lowernode;

2431

2441 else t

2451 --- search key > highest key in node

2461 if (trnode->header.isleaf)

2471 break;

2481 currnode = trnode->keys.LastEntry()->lowernode;

2491 1

2501 }

2511 return false;

2521

2531

2541 // delete a key from a btree

Page 304: C++ Database Development 1558283579

Appendix C Software Listings 299

2551 void PdyBtree::Delete(PdyKey *keypointer)

2561

2571 if (Find(keypointer)) 1

2581 if (!trnode->header.isleaf) f

2591

2601 --- if not found in leaf node, go down to leaf

2611 TNode *leaf =

2621 new TNode(this, trnode->currkey->lowernode);

2631 while (!leaf->header.isleaf) f

2641 NodeNbr If = leaf->header.lowernode;

2651 delete leaf;

2661 leaf = new TNode(this, lf):

2671

2681

2691 ---- Move the left-most key from the leaf

2701 / to where deleted key was in higher node

2711 PdyKey *movekey - leaf->keys.FirstEntryo);

2721 leaf->keys.RemoveEntry(movekey);

2731 leaf->header.keycount--;

2741 leaf->MarkNodeChangedo;

2751

2761 trnode->keys.InsertEntry(movekey, trnode->currkey);

2771

2781 movekey->lowernode = trnode->currkey->lowernode;

2791

2801 trnode->keys.RemoveEntry(trnode->currkey);

2811 delete trnode->currkey;

2821 trnode->MarkNodeChanged();

2831 delete trnode;

2841

2851 trnode = leaf;

2861 trnode->currkey = trnode->keys.FirstEntryo;

2871 currnode = trnode->GetNodeNbr(;

2881

2891 else f

2901 // delete the key from the node

2911 trnode->keys.RemoveEntry(trnode->currkey);

2921 delete trnode->currkey;

Page 305: C++ Database Development 1558283579

300 C++ Database Development

2931 trnode->header.keycount--;

2941 trnode->MarkNodeChangedo;

2951 if (trnode->header.keycount == 0)

2961 header.rootnode = 0;

2971

2981 // if the node shrinks to half capacity,

2991 / try to combine it with a sibling node

3001 while (trnode->header.keycount > 0 &&

3011 trnode->header.keycount <= trnode->m)/2) {

3021 if (trnode->header.rightsibling) {

3031 TNode *right =

3041 new TNode(this,trnode->header.rightsibling);

3051 if (trnode->Implode(*right)) {

3061 delete right;

3071 NodeNbr parent - trnode->header.parent;

3081 if (parent -- 0) 1

3091 header.rootnode = trnode->GetNodeNbro;

3101 break;

3111 }

3121 delete trnode;

3131 trnode = new TNode(this, parent);

3141 continue;

3151

3161 delete right;

3171

3181 if (trnode->header.leftsibling) t

3191 TNode *left =

3201 new TNode(this, trnode->header.leftsibling);

3211 if (left->Implode(*trnode)) t

3221 delete trnode;

3231 NodeNbr parent = left->header.parent;

3241 if (parent =- 0) {

3251 header.rootnode - left->GetNodeNbr(;

3261 trnode - left;

3271 break;

3281

3291 delete left;

3301 trnode - new TNode(this, parent);

Page 306: C++ Database Development 1558283579

Appendix C Software Listings 301

3311 continue;

3321

3331 delete left;

334 }335

3361 --- could not combine with either sibling,

3371 / try to redistribute

3381 if (!trnode->Redistribute(

3391 trnode->header.leftsibling))

3401 trnode->Redistribute(

3411 trnode->header.rightsibling);

3421 break-

3431

3441

3451 delete trnode;

3461 trnode = 0;

3471 1

3481

3491 // return the address of the current key

3501 PdyKey *PdyBtree::Current()

3511 {

3521 if (trnode == 0)

3531 return 0;

3541 if (oldcurrnode != 0) {3551 currnode = oldcurrnode;

3561 delete trnode;

3571 trnode = new TNode(this, currnode);3581 trnode->currkey = trnode->keys.FindEntry(oldcurrkey);

3591 oldcurrnode = 0;

3601 oldcurrkey = 0;

3611

3621 return trnode->currkey;

3631 }

3641

3651 // ---- return the address of the first key

3661 PdyKey *PdyBtree::First()

3671

3681 currnode = header.rootnode;

Page 307: C++ Database Development 1558283579

302 c++ Database Development

3691 if (currnode) {

3701 delete trnode;3711 trnode - new TNode(this, currnode);

3721 while (!trnode->header.isleaf) {

3731 currnode - trnode->header.lowernode;

3741 delete trnode;

3751 trnode = new TNode(this, currnode);3761

3771 trnode->currkey = trnode->keys.FirstEntryo;

3781 1

3791 return Current();3801 1

3811

3821 // ------- return the address of the last key

3831 PdyKey *PdyBtree::Last()3841 [

3851 currnode - header.rootnode;

3861 if (currnode) t

3871 delete trnode;

3881 trnode - new TNode(this, currnode);

3891 while (!trnode->header.isleaf) {

3901 currnode - trnode->keys.LastEntry()->lowernode;

3911 delete trnode;

3921 trnode - new TNode(this, currnode);

3931 1

3941 trnode->currkey = trnode->keys.LastEntryo);

39513961 return Currento);

3971 1

3981

3991 // --- return the address of the next key

4001 PdyKey *PdyBtree::Next()

4011

4021 if (trnode -= 0 11 trnode->currkey == 0)

4031 return Firsto);

4041 if (!trnode->header.isleaf) {

4051 // --- current key is not in a leaf4061 currnode - trnode->currkey->lowernode;

Page 308: C++ Database Development 1558283579

Appendix C Software Listings 303

4071 delete trnode;

4081 trnode = new TNode(this, currnode);

4091 // go down to the leaf

4101 while (!trnode->header.isleaf) {

4111 currnode - trnode->header.lowernode;

4121 delete trnode;

4131 trnode = new TNode(this, currnode);

4141

4151 // use the first key in the leaf as the next one

4161 trnode->currkey = trnode->keys.FirstEntry();

4171

4181 else {

4191 /-/ current key is in a leaf

4201 PdyKey *thiskey = nullkey->MakeKeyo;

4211 *thiskey = *(trnode->currkey);

4221

4231 ----- point to the next key in the leaf

4241 trnode->currkey =

4251 trnode->keys.NextEntry(trnode->currkey);

4261 while (trnode->currkey == 0 &&

4271 currnode != header.rootnode)

4281 --- current key was the last one in the leaf

4291 TNode pnode(this, trnode->Parento);

4301 pnode.SearchNode(thiskey);

4311 currnode - pnode.GetNodeNbro;

4321 *trnode = pnode;

4331 1

4341 delete thiskey;

4351 }

4361 return Currento);

4371

4381

4391 // return the address of the previous key

4401 PdyKey *PdyBtree::Previous()

4411 f

4421 if (trnode == 0 11 trnode->currkey -- 0)

4431 return Last);

4441 if (!trnode->header.isleaf) t

Page 309: C++ Database Development 1558283579

304 C++ Database Development

4451 // --- current key is not in a leaf

4461 PdyKey *ky - trnode->keys.PrevEntry(trnode->currkey);

4471 if (ky !- 0)

4481 currnode = ky->lowernode;

4491 else

4501 currnode = trnode->header.lowernode;

4511 delete trnode;4521 trnode = new TNode(this, currnode);

4531 // ---- go down to the leaf

4541 while (!trnode->header.isleaf) {4551 currnode - trnode->keys.LastEntry()->lowernode;

4561 delete trnode;

4571 trnode = new TNode(this, currnode);

4581

4591 // use the last key in the leaf as the next one4601 trnode->currkey = trnode->keys.LastEntry(;

46114621 else {

4631 / -/ current key is in a leaf

4641 PdyKey *thiskey = nullkey->MakeKeyo);

4651 *thiskey = *(trnode->currkey);

4661

4671 ----- point to the previous key in the leaf

4681 trnode->currkey =

4691 trnode->keys.PrevEntry(trnode->currkey);

4701 while (trnode->currkey -- 0 &&

4711 currnode != header.rootnode)

4721 --- current key was the first one in the leaf

4731 TNode pnode(this, trnode->Parent();

4741 pnode.SearchNode(thiskey);

4751

4761 if (pnode.currkey == 0)

4771 pnode.currkey = pnode.keys.LastEntryo;4781 else

4791 pnode.currkey =

4801 pnode.keys.PrevEntry(pnode.currkey);

4811 currnode = pnode.GetNodeNbro);

4821 *trnode = pnode;

Page 310: C++ Database Development 1558283579

Appendix C: Software Listings 305

jdelete thiskey;

rreturn Current();

// node.h

#ifndef NODE H

#define NODE H

typedef unsigned short int NodeNbr;

const short int nodelength - 128;

const short int nodedatalength = nodelength - sizeof(NodeNbr);

// -- exceptions to be thrown

class BadFileOpen{};

class FileReadErrort};

class FileWriteError{};

I121

31

41

51

61

71

81

91

101

11i

121

131

141

15I

161

171

181

191

201

211

221

231

241

251

261

271

281

291

301

class FileHeader {

NodeNbr deletednode;

NodeNbr highestnode;

friend class NodeFile;

FileHeader() { deletednode

II first deleted node

II highest assigned node

= highestnode = 0; 1

Node File Header Class

4831

4841

4851

4861

4871

LISTING C.15 NODE.H

Node File Header Record

II

I-

II

II;

I-

II

class NodeFile {

FileHeader header;

FileHeader origheader;

Page 311: C++ Database Development 1558283579

306 C++ Database Development

311 fstream nfile;

321 bool newfile; // true if building new node file

331 public:

341 NodeFile(const string& filename) throw (BadFileOpen);

351 virtual -NodeFileo;

361 void SetDeletedNode(NodeNbr node)

371 { header.deletednode = node; }

381 NodeNbr DeletedNodeo) const

391 t return header.deletednode; }

401 void SetHighestNode(NodeNbr node)

411 t header.highestnode = node; }

421 NodeNbr HighestNode() const

431 return header.highestnode; I

441 NodeNbr NewNodeo);

451 void ReadData(void *buf,

461 unsigned short siz, long wh = -1)

471 throw (FileReadError);

481 void WriteData(const void *buf,

491 unsigned short siz, long wh = -1)

501 throw (FileWriteError);

511 void Seek(streampos offset, ios::seek dir dir = ios::beg)

521 t nfile.seekg(offset,dir); nfile.seekp(offset,dir); }

531 streampos FilePosition()

541 t return nfile.tellg( ; J

551 bool NewFile() const

561 t return newfile; }

571 void ResetNewFile()

581 { newfile = false; }

591 };

60

611 //

621 //

631 //641 c1

651

661

671 prr

681

Node Record

ass Node t

NodeNbr nextnode;

void CloseNode);

otected:

NodeFile *owner;

Page 312: C++ Database Development 1558283579

Appendix C: Software Listings 307

691

701711721731741

751761

771

781

791

801811

821

831

841

851

861871

881

891901

911

921

931

941

951

nged

being deleted

STING C.16 NODE.CPP

I //21

-- node.cpp

//

// Parody Node and NodeFile class member functions//

61

71 #include <io.h>81

NodeNbr nodenbr; // current node number

bool nodechanged; // true if the node cha

bool deletenode; // true if the node is

public:

Node(NodeFile *hd = 0, NodeNbr node - 0);

virtual -Node();

Node& operator-(Node& node);

void SetNextNode(NodeNbr node)

f nextnode = node; MarkNodeChangedo; }

NodeNbr NextNode() const

f return nextnode; }

void SetNodeNbr(NodeNbr node)

{ nodenbr - node; }

NodeNbr GetNodeNbro) const

t return nodenbr; }

void MarkNodeDeleted()

f deletenode - true;

void MarkNodeChangedo)

t nodechanged = true; }

bool NodeChanged() const

f return nodechanged; I

long NodeAddress();

virtual short int NodeHeaderSizeo) const

I return sizeof(NodeNbr); }

};

#endi f

31

4151

------------ ---

------------- -------------------------

Page 313: C++ Database Development 1558283579

308 c++ Database Development

91 #include "parody.h"10

111 // - construct a node file

121 NodeFile::NodeFile(const string& filename) throw (BadFileOpen)

131 {

141 newfile - access(filename.c-str(). 0) != 0;151 // open the file

161 nfile.open(filename.c stro,

171 ios::in I ios::out I ios::binary);

181 if (nfile.fail())

191 throw BadFileOpeno;

201 if (!newfile)

211 // - an existing file, read the header

221 ReadData(&header, sizeof header);

231 else241 // creating the file, write the empty header251 WriteData(&header, sizeof header);

261 origheader = header;

271

281

291 NodeFile::-NodeFileo)

301 {

311 if (header.deletednode != origheader.deletednode 1I

321 header.highestnode != origheader.highestnode) t

331 // the file header has changed

341 WriteData(&header, sizeof header, 0);

351 }361 nfile.closeo;

371 1

381

391 void NodeFile::ReadData(void *buf,

401 unsigned short siz, long wh) throw (FileReadError)

411 {

421 if (wh !- -1)

431 nfile.seekg(wh);

441 nfile.read(reinterpret cast<char*>(buf), siz);451 if (nfile.fail() 11 nfile.eof()) {461 nfile.clearo;

Page 314: C++ Database Development 1558283579

Appendix C Software Listings 309

471 throw FileReadErroro;

481 1

491 nfile.seekp(nfile.tellg ));

501 1511

521 void NodeFile::WriteData(const void *buf,

531 unsigned short siz, long wh) throw (FileWriteError)

541 {

551 if (wh != -1)561 nfile.seekp(wh);

571 nfile.write(reinterpret-cast<const char*>(buf), siz);581 if (nfile.fail()) t591 nfile.clear();

601 throw FileWriteErroro;

611621 nfile.seekg(nfile.tellp());

631 1

641

651 // ------- appropriate a new node661 NodeNbr NodeFile::NewNode()

671 {

681 NodeNbr newnode;

691 if (header.deletednode) t

701 newnode = header.deletednode;

711 Node node(this, newnode);

721 header.deletednode - node.NextNode);

731 node.SetNextNode(O);

741 1

751 else761 newnode - ++header.highestnode;

771 return newnode;

781

791801 // ------ construct a new node

811 Node::Node(NodeFile *hd, NodeNbr node)

821 {

831 nextnode = 0;

841 nodechanged = deletenode - false;

Page 315: C++ Database Development 1558283579

310 c++ Database Development

851 nodenbr - node;

861 owner - hd;

871 if (nodenbr) t

881 long nad - NodeAddress();

891 // ------- read the header

901 try t

911 owner->ReadData(&nextnode, sizeof nextnode, nad);

921 }

931 catch (FileReadError) t

941 // ----- appending a new node

951 owner->WriteData(&nextnode, sizeof nextnode, nad);

961

971 1

981 1

9911001 // ----- close a node

101 void Node::CloseNode()

1021 t

1031 if (owner && nodenbr && (nodechanged II deletenode))1041 if (deletenode) f

1051 nextnode - owner->DeletedNode(;

1061 owner->SetDeletedNode(nodenbr);

1071

1081 long nad - NodeAddress(;

1091 // ------- write the header

1101 owner->WriteData(&nextnode, sizeof nextnode, nad);1111 if (deletenode) t

1121 // ------ zero fill the deleted node

1131 char fill[nodedatalength];

1141 memset(fill, 0. nodedatalength);

1151 fill[O] - -1; // mark the node deleted

1161 owner->WriteData(fill, nodedatalength);

1171

1181 1

1191 1

1201

1211 // ------- assignment operator

1221 Node& Node::operator-(Node& node)

Page 316: C++ Database Development 1558283579

Appendix C Software Listings 311

1231 {

1241 CloseNodeo;1251 nextnode = node.nextnode;

1261 owner = node.owner;

1271 nodenbr = node.nodenbr;

1281 nodechanged = node.nodechanged;

1291 deletenode = node.deletenode;

1301 return *this;

1311 1

1321

1331 // destroy the node

1341 Node::-Node()

1351 {

1361 CloseNodeo;

1371 11381

1391 // compute the disk address of a node

1401 long Node::NodeAddress()

1411 {

1421 long adr = nodenbr-l;

1431 adr *= nodelength;

1441 adr += sizeof(FileHeader);

1451 return adr;1461 )

LISTING C.17 TNODE.CPP

1 /------------- tnode.cpp

21

31 /

41 / B-tree Tnode class

5 //61

71 #include "parody.h"

8191 TNode::TNode(PdyBtree *bt, NodeNbr nd)

101 Node(&(bt->GetlndexFileo), nd)

il l {

Page 317: C++ Database Development 1558283579

312 C++ Database Development

121 btree = bt;

131 currkey - 0;

141 IndexFile& nx = btree->GetlndexFile();

151 long nad - NodeAddresso) + Node::NodeHeaderSize();

161 // -------- read the header

171 try {

181 nx.ReadData(&header, sizeof(TNodeHeader), nad);191 }

201 catch (FileReadError) t

211 // ------ appending a new node

221 nx.WriteData(&header, sizeof(TNodeHeader), nad);

231 return;

241 1

251 // --- reading an existing node, read the keys

261 for (int i = 0; i < header.keycount; i++) {

271 // - get memory for and read a key

281 PdyKey *thiskey = btree->MakeKeyBuffero;

291 thiskey->ReadKey(nx);

301

311 ---- read the key's file address

321 NodeNbr fa;

331 nx.ReadData(&fa, sizeof(NodeNbr));341 thiskey->fileaddr = fa;

351361 if (lheader.isleaf) t

371 NodeNbr Inode;381 nx.ReadData(&lnode, sizeof(NodeNbr)):

391 thiskey->lowernode = lnode;401411 keys.AppendEntry(thiskey);

421431

441

451 // -- -- write a key to the node's disk record

461 void TNode::WriteBtreeKey(PdyKey *thiskey)

471 {

481 IndexFile& nx - btree->GetlndexFileo);

491 // ---- write the key value

Page 318: C++ Database Development 1558283579

Appendix C Software Listings 313

501 thiskey->WriteKey(nx);

511 // write the key's file address

521 NodeNbr fa = thiskey->fileaddr;

531 nx.WriteData(&fa, sizeof(NodeNbr));

541 if (!header.isleaf) {

551 // --- write the lower node pointer for non-leaf keys

561 NodeNbr Inode = thiskey->lowernode;571 nx.WriteData(&Inode, sizeof(NodeNbr));

581

591

601611 TNode::-TNode()

621 {631 if (header.keycount -- 0)

641 // ---- this node is to be deleted

651 deletenode = true;

661 else t

671 IndexFile& nx = btree->GetlndexFileO;

681 if (nodechanged) {

691 long nad = NodeAddresso) + Node::NodeHeaderSize();

701 // ------ write the node header

711 nx.WriteData(&header, sizeof(TNodeHeader). nad);

721 1

731 // write the keys

741 PdyKey *thiskey =keys.FirstEntry();

75 while (thiskey 0= 0)

761 if (nodechanged)

77 WriteBtreeKey(thiskey);781 delete thiskey;

791 thiskey = keys.NextEntryo;801

811 if (nodechanged) f

821 // ----- pad the node

831 short int keyspace = header.keycount *

841 (btree->GetKeyLength() + sizeof(NodeNbr) +851 (header.isleaf ? 0 : sizeof(NodeNbr)));

861 int residual - nodedatalength -871 keyspace - sizeof(TNodeHeader);

Page 319: C++ Database Development 1558283579

314 c++ Database Development

881 char *fill = new char[residuall;891 memset(fill, 0. residual);

901 nx.WriteData(fill, residual);

911 delete fill;

921 }

931 j

941

951961 / ---- assignment operator

971 TNode& TNode::operator-(TNode& tnode)981

991 PdyKey *thiskey = keys.FirstEntry();1001 // -- if receiver has any keys, delete them

101 while (header.keycount > 0) f

1021 delete thiskey:

1031 -- header.keycount;

1041 thiskey = keys.NextEntry();

1051 }

1061 keys.ClearListo;

107 Node::operator-(tnode);

1081 header = tnode.header;

1091 currkey = 0;1101 // -- copy the keys

1111 thiskey = tnode.keys.FirstEntry();

1121 while (thiskey 1= 0) {

1131 PdyKey *newkey = btree->MakeKeyBuffero;

1141 *newkey = *thiskey;

1151 keys.AppendEntry(newkey);

1161 if (thiskey == tnode.currkey)

1171 currkey = newkey;

1181 thiskey = tnode.keys.NextEntryo);1191

1201 return *this;

1211 }

1221

1231 -------- compute m value of node

1241 short int TNode::m()

1251 t

Page 320: C++ Database Development 1558283579

Appendix C: Software Listings 315

1261 int keyspace - nodelength - NodeHeaderSizeo);

1271 int keylen - btree->GetKeyLengtho);

1281 if (!header.isleaf)

1291 keylen +- sizeof(NodeNbr);

1301 return keyspace / keylen;

1311 1

1321

1331 // ----------- search a node for a match on a key

1341 bool TNode::SearchNode(PdyKey *keyvalue)

1351

1361 currkey - keys.FirstEntryo;

1371 while (currkey !0 0) t

1381 if (*currkey > *keyvalue)

1391 break;

1401 if (*currkey -- *keyvalue) {

1411 if (keyvalue->indexno == 0)

1421 return true;

1431 if (currkey->fileaddr == keyvalue->fileaddr)

1441 return true;

1451 if (keyvalue->fileaddr == 0)

1461 return true;

1471 if (currkey->fileaddr > keyvalue->fileaddr)

1481 break;

1491

1501 currkey = keys.NextEntry(;

1511

1521 return false;

1531

1541

1551 void TNode::Insert(PdyKey *keyvalue)

156!

157 /-------- insert the new key

1581 PdyKey *ky - keyvalue->MakeKeyo;

1591 *ky - *keyvalue;

1601 if (currkey -- 0)

1611 keys.AppendEntry(ky);

1621 else

1631 keys.InsertEntry(ky, currkey);

Page 321: C++ Database Development 1558283579

316 C++ Database Development

1641 header.keycount++;

1651 nodechanged = true;

1661 currkey = ky;

1671 }

1681

1691 // a node "adopts" all its children by telling

1701 // them to point to it as their parent

1711 void TNode::Adoption()

1721 f

1731 Adopt(header.lowernode);

1741 PdyKey *thiskey = keys.FirstEntryo;

1751 for (nt i = 0; i < header.keycount; i++) {

1761 Adopt(thiskey->lowernode);

1771 thiskey = keys.NextEntry(;

1781 1

1791 1

1801

1811 // --- adopt a child node

1821 void TNode::Adopt(NodeNbr node)

1831 {

1841 if (node) {

1851 TNode nd(btree, node);

1861 nd.header.parent = nodenbr;

1871 nd.nodechanged = true;

1881

1891 1

1901

1911 // - redistribute keys among two sibling nodes

1921 bool TNode::Redistribute(NodeNbr sib)

1931 {

1941 if (sib =-= 0)

1951 return false;

1961 TNode sibling(btree, sib);

197

1981 if (sibling.header.parent != header.parent)

1991 return false;

2001

2011 int totkeys = header.keycount + sibling.header.keycount;

Page 322: C++ Database Development 1558283579

Appendix C Software Listings 317

2021 if (totkeys >= m() * 2)

2031 return false:

2041

2051 ---- assign left and right associations

2061 TNode *left, *right;

2071 if (sib == header.leftsibling) {

2081 right = this;

2091 left = &sibling;

2101 12111 else t

2121 right = &sibling;

2131 left = this;

2141 }

2151 // compute number of keys to be in left node

2161 int leftct =

2171 (left->header.keycount + right->header.keycount) / 2;

2181 /-/ if no redistribution would occur

2191 if (leftct == left->header.keycount)

2201 return false;

2211 //-- compute number of keys to be in right node

2221 int rightct =

2231 (left->header.keycount+right->header.keycount)-leftct;

2241 ------- get the parent

2251 TNode parent(btree, left->header.parent);

2261 --- position parent's currkey

2271 // to one that points to siblings

2281 parent.SearchNode(left->keys.FirstEntry ));

2291 // will move keys from left to right or right to

2301 // left depending on which node has the greater

2311 // number of keys to start with.

2321 if (left->header.keycount < right->header.keycount) {

2331 // ----- moving keys from right to left

2341 int mvkeys = right->header.keycount - rightct - 1;

2351 // ----- move key from parent to end of left node

2361 left->currkey = parent.currkey;

2371 parent.currkey = parent.keys.NextEntry(parent.currkey);

2381 // remove parent key from its list

2391 parent.keys.RemoveEntry(left->currkey);

Page 323: C++ Database Development 1558283579

318 C++ Database Development

240! // --- put it in left node's list

2411 left->keys.AppendEntry(left->currkey);

2421

2431 if (!left->header.isleaf)

2441 left->currkey->lowernode - right->header.lowernode;

2451 --- point to the keys to move

2461 / (at front of right node)

2471 PdyKey *movekey - right->keys.FirstEntryo;

248! // -.-- move keys from right to left node

249! for (nt i 0 0; i < mvkeys; i++) f

2501 PdyKey *nkey - right->keys.NextEntry(movekey);

2511 right->keys.RemoveEntry(movekey);

252! left->keys.AppendEntry(movekey);

253! movekey - nkey;

254 }

255! --- move separating key from right node to parent

256! right->keys.RemoveEntry(movekey);

257! parent.keys.InsertEntry(movekey, parent.currkey);

258! if (!right->header.isleaf)

259! right->header.lowernode - movekey->lowernode;

260! movekey->lowernode - right->nodenbr;

261! right->header.keycount - rightct;

262! left->header.keycount - leftct;

263! if (!left->header.isleaf)

264! left->Adoption();

265!

266! else {

267 /-------- moving from left to right

268 int mvkeys - left->header.keycount - leftct - 1;

269! // ----- move key from parent to right node

270! right->currkey - parent.currkey;

271! parent.currkey - parent.keys.NextEntry(parent.currkey);

272! // --- remove parent key from its list

273! parent.keys.RemoveEntry(right->currkey);

274! // ---- put it in right node's list

275! right->keys.InsertEntry(right->currkey,

276! right->keys. FirstEntry());

277! if (!right->header.isleaf)

278! right->currkey->lowernode-right->header.lowernode;

Page 324: C++ Database Development 1558283579

Appendix C Software Listings 319

2791 // locate the first key to move in the left node

2801 PdyKey *movekey = left->keys.FindEntry(leftct);

2811 // --- remember the key after the one being moved up

2821 PdyKey *nkey = left->keys.NextEntry(movekey);

2831 // -- move key from left node up to parent

2841 left->keys.RemoveEntry(movekey);

2851 parent.keys.InsertEntry(movekey, parent.currkey);

2861

2871 right->header.lowernode - movekey->lowernode;

2881 movekey->lowernode = right->nodenbr;

2891 movekey = nkey;

2901 // --- move keys from the left node to the right node2911 PdyKey *inskey = right->keys.FirstEntry();

2921 for (int i = 0; i < mvkeys; i++) I

2931 PdyKey *nkey = left->keys.NextEntry(movekey);

2941 left->keys.RemoveEntry(movekey);

2951 right->keys.InsertEntry(movekey, inskey);

2961 movekey = nkey;

2971

2981 right->header.keycount = rightct;

2991 left->header.keycount = leftct;

3001 if (!right->header.isleaf)

3011 right->Adoption(;3021

3031 nodechanged =

3041 sibling.nodechanged =

3051 parent.nodechanged = true;

3061 return true;

3071 1

30813091 // implode the keys of two sibling nodes

3101 bool TNode::Implode(TNode& right)

3111 f

3121 int totkeys = right.header.keycount+header.keycount;

3131 if (totkeys >= m() II3141 right.header.parent != header.parent)

3151 return false;

3161 nodechanged = right.nodechanged = true;

3171 header.rightsibling = right.header.rightsibling;

Page 325: C++ Database Development 1558283579

320 C++ Database Development

3181 header.keycount +- right.header.keycount+l;

3191 right.header.keycount - 0;

3201 // --- get the parent of the imploding nodes

3211 TNode parent(btree, header.parent);

3221 parent.nodechanged = true;

3231 ---- position parent's currkey to

3241 / key that points to siblings

3251 parent.SearchNode(keys.FirstEntryo);

3261 // --- move the parent's key to the left sibling

3271 parent.keys.RemoveEntry(currkey);

3281 keys.AppendEntry(parent.currkey);

3291 parent.currkey->lowernode - right.header.lowernode;

3301 parent.header.keycount--;

3311 if (parent.header.keycount -- 0)

3321 // -- combined the last two leaf nodes into a new root

3331 header.parent = 0;

3341 -- move the keys from the right sibling into the left

3351 PdyKey *movekey - right.keys.FirstEntryo;

3361 while (movekey 0) {

3371 PdyKey *nkey = right.keys.NextEntry(movekey);

3381 right.keys.RemoveEntry(movekey);

3391 keys.AppendEntry(movekey);3401 movekey = nkey;

3411 1

3421 if (header.rightsibling) {

3431 // - point right sibling of old right to imploded node

3441 TNode farright(btree, header.rightsibling);

3451 farright.header.leftsibling - GetNodeNbr();

3461 farright.nodechanged - true;

3471

3481 Adoption);

3491 return true;

3501 O

LISTING C.18 PAYROLL.H

11 II payroll.h21

Page 326: C++ Database Development 1558283579

Appendix C Software Listings 321

31 #ifndef PAYROLL H

41 #define PAYROLLH

5161 #include "parody.h"

71 #include "gui.h"81

91 class SSN {101 long ssn;

11 public:

121 SSN(long s=O) : ssn(s)

131 f /* ... 1

141 friend ostream& operator<<(ostream&os, const SSN& sn);

151 1;161

171 const short NameLength = 15;

181

191 struct PayrollRcd {

201 SSN ssn; // Social Security Number

211 char name[NameLength+l]; // Employee name

221 Date pmtdate; 1/ Date paid231 Money wage; // Hourly wage

241 short reghours; // regular hours worked251 short othours; // overtime hours worked

261 static void Header(ostream& os = cout);

271 friend ostream& operator<<(ostream&os,const PayrollRcd&pr);

281 J;

291

301 #endif

LISTING C.19 PAYROLL.CPP

11 // --- payroll.cpp

21

31 #include <iostream.h>

41 #include <string.h>

51 #include "payroll.h"61

71 GUI *gui;

Page 327: C++ Database Development 1558283579

322 C++ Database Development

8I91 void PayrollRcd::Header(ostream& os)

101 t

III os <<

121 "SSN Name Date Wage Reg OT";

131 os << endl;

141 os <<1 5 1 .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..- - "--

161 os << endl;

171

181

191 ostream& operator<<(ostream&os, const PayrolIRcd& pr)

201

211 os << pr.ssn;

221 os << ';231 os.width(NameLength);

241 os.setf(ios::left);251 os << pr.name;

261 os << ';271 os << pr.pmtdate;

281 os << , ';

291 os << pr.wage;

301 os << . ;311 os.width(2);321 os.setf(ios::right);

331 os << pr.reghours;

341 os << « ;351 os-width(2);

36 os << pr.othours;

371 os << endl;

381 return os;

391401

411 stream& operator<<(ostream&os, const SSN& sn)

421 {

431 os << sn.ssn/1000000 << '-' <<

441 (sn.ssn%1000000)/10000 << - << sn.ssn%10000;

451 return os;

Page 328: C++ Database Development 1558283579

Appendix C: Software Listings 323

461 1

471

481 void AddRe

491 void ListR

501

511 int main()

521 {53 gui =

541 Parody

551

561 Screenl

571

581

591

601

611 gui->C

621 delete

631 delete

641 return

65j 1

661

671 void AddRe

681 {691 Payrol

701 long s

711 gui->U

721 pr.ssn

731 string

741 gui ->U

751 strncp.

761 pr.nam

771 gui->U

781 gui ->U

791 gui->U

801 gui->U

811 Payrol

821 cout <

831 if (gu

cord( );

ecords();

new GUI;*payroll - new Parody("PAYROLL");

Menu("Payroll".

"Add record", AddRecord,

"List records", ListRecords,NULL).Execute();

learScreen();

payroll;

gui0;

cord( )

IRcd pr;

sn;

serlnput(&ssn, "SSN

= ssn;

name;

serlnput(&name, "Nam

y(pr.name, name.c str(o, Na

e[NameLength] = '\0';

serlnput(&pr.pmtdate, "Dat

serlnput(&pr.wage, "Wag

serlnput(&pr.reghours, "Reg

serlnput(&pr.othours, "Ove

1Rcd::Header();

< pr;

i->YesNo("Add record")) {

(nnnnnnnnn)");

e

meLength);

e ");

e

ular Hours

rtime Hours

NameLength);

Page 329: C++ Database Development 1558283579

324 c++ Database Development

841 PersistentObject<PayrollRcd> ppr(pr);

851 ppr.AddObjecto;

861

871 1

881

891 void ListRecords()

90! {911 PayrollRcd::Header( ;

921 PersistentObject<PayrollRcd> ppr;

931 ppr.FirstObject();

941 while (ppr.ObjectExists() {

951 cout << ppr.Obj;

961 ppr.NextObjecto;

971 1

981 gui->AnyKey():991 )

LISTING C.20 LIFE.CPP

I // ----------------- life.cpp

21

31 //

4 1/ The Game of Life - a simulation

51 /61

71 #include <iostream.h>

81 #include <strstrea.h>

91 #include <iomanip.h>101 #include <ctype.h>Ill #include "parody.h"

121 #include "gui.h"

131

141 // ---- dimensions of the Life world

151 const short HT = SCREENHEIGHT-1;

161 const short WD = SCREENWIDTH;

171

181 const short keylen = 6; // length of the name string key

191 const char Pop = 2; // the populated display character

Page 330: C++ Database Development 1558283579

20

211

22!

231

241

251

261

271

281

291

301

311

321

331

341

351

36!

371

381

391

40

41421

431441

451

46471

481

491

5O0

511

521

531541

551

56!

571

I;

GUI *gui

void Life

strinReadO

name.

short

// -- collection of neighboring cells

class Life : public Persistent t

Key<string> name;

unsigned char world[WDI[HT];

short generation;

bool alive;

short popcount;

static short Rules[2][9];

short CountNeighbors(short x, short y) c

void Reado;

void Writeo);

public:

Life(const string& nm = string('\O', key

-Life()

{ SaveObject(); I

void Generationo);

bool isAliveo) const

{ return alive; I

void Populate(short x, short y);

void DePopulate(short x, short y);

bool isPopulated(short x, short y) cost

t return world[xl[y]; I

void SetName(const string& nm)

{ name.SetKeyValue(nm); I

string Name()

t return name.KeyValue(; I

len));

read the persistent Life object

::Read()

g nm;

bject(nm)

SetKeyValue(nm);

ct, x, y;

Appendix C: Software Listings 325

onst;

Page 331: C++ Database Development 1558283579

326 C++ Database Development

581 ReadObject(ct);

591 while (ct--) {601 ReadObject(x);

611 ReadObject(y);

621 Populate(x,y);

631 }

641 1

651

661 // ------- write the persistent Life object

671 void Life::Write()

68 1

691 WriteObject(name.KeyValueo);

701 WriteObject(popcount);

711 for (short x - 0; x < WD; x++)

721 for (short y = 0; y < HT; y++) {

731 if (world[x][y])

741 Write0bject(x);

751 WriteObject(y);761

771

781 1791 1801

811 // ------- rules for cell survival

821 short Life::Rules[2][9] - t831 // --- cell is unpopulated

841 {0.0,0,2,0,0.0,0.01,

851 // --- cell is populated

861 {0.0,2.2,0.0.0,0,01

871 J;

881

891 Life::Life(const string& nm) : name(nm)

901 t911 for (short x = 0; x < WD; x++)

921 for (short y - 0; y < HT; y++)

931 world[x][y] - 0;

941 alive = false;

951 generation = 0;

Page 332: C++ Database Development 1558283579

961971

981

9911001

101l

1021

1031

1041

1051

1061

1071

1081

1091

1131112j

1131

1141

1151

11611171

1181

1191

1201

1211

1221

12311241

1251

1261

1271

1281

1291

1301

1311

1321

1331

// -for

--- count 3 rows

(yl = y-1; yl < y+2; yl++)

// count 3 columnsfor (xl - x-1; xl < x+2; xl++)

// --- don't count self

if (!(yl == y && xl == x))

// --- wrap around margins

int x2 = xl, y2 - yl;

if (x2 == -1)

x2 = WD-1;

else if (x2 == WD)

x2 = 0;

if (y2== -1)

Appendix C: Software listings 327

popcount 0 0;

LoadObject();

-/ populate a cellvoid Life::Populate(short x, short y){

if (!world[x][y])

popcount++;

world~x][y] = 1;

alive - true;

// ----- depopulate a cell

void Life::DePopulate(short x, short y)

t

if (world[x][y])

-- popcount;

world[x][y] = 0;}

// count a cell's neighbors

short Life::CountNeighbors(short x, short y) const

{short x1, yl, ct = 0;

{

Page 333: C++ Database Development 1558283579

328 C++ Database Development

1341 y2 = HT-1;

1351 else if (y2 == HT)

1361 y2 = 0;

1371 --- count populated neighbor cells

1381 if ((world[x2][y2]) & 1)

1391 ct++;

1401 1

1411 return ct;

1421

1431

1441 ------- evolve one generation

1451 void Life::Generation()

1461 t

1471 register unsigned char *cell;

1481 short x, y;1491 gui->SetCursor(0, HT);

1501 char st[81];

1511 strstream stat(st, 80, ios::out);

1521 stat << "Generation: " << generation++ << '\0';

1531 gui->StatusLine(stat.str( );1541 for (cell = world[0], x = 0; x < WD; x++)

1551 for (y = 0; y < HT; y++, cell++)

1561 *cell I= Rules[*cell][CountNeighbors(x, y)];

1571 alive = false;

1581 popcount - 0;1591 for (cell = world[0], x = 0; x < WD; x++) {

1601 for (y = 0; y < HT; y++, cell++) {

1611 switch (*cell) {

1621 case 1:

1631 gui->WriteChar(' ', x, y);

1641 *cell - 0;

1651 break;

1661 case 2:

1671 gui->WriteChar(Pop, x, y);

1681 case 3:

1691 if (*cell == 2)

1701 alive = true;

1711 *cell = 1;

Page 334: C++ Database Development 1558283579

Appendix C: Software Listings 329

1721

1731

1741

1751

1761

1771

1781 1

1791 1

1801

1811 void Read

1821 void Buil

1831 void Chan

1841 void Dele

1851 void List

1861 void Disp

1871 void RunL

1881 void Step

1891 void Load

1901 void Save

1911

1921 / - a]

1931 Life *If;

1941

1951 int main(

1961

1971 Parod

1981 gui =

1991 if (a

2001 g

2011 R

2021 1

2031 Scree

2041

2051

2061

2071

2081

2091

popcount++;

break;

default:

break;

I

Life(const

dLife( );

geLife( )

teLife( )

Lives();

1 ayLi fe();i fe();

Life();

Life();

Life();

string& nm);

ways -> current Life object

int argc, char *argv[])

y life("LIFE");

new GUI;

rgc > 1) {

ui->ClearScreen();

eadLife(argv[1]);

nMenu( "The Game of"New",

"Change",

"Del ete",

"List",

"Di spl ay","Run",

Life",BuildLife.

ChangeLife,

DeleteLife,

ListLives,

DisplayLife,

RunLife,

Page 335: C++ Database Development 1558283579

330 c++ Database Development

2101 "Step", StepLife,

2111 "Load", LoadLife,2121 "Save", SaveLife,

2131 NULL).Executeo;

2141 gui->ClearScreen();

2151 delete gui;

2161 delete If;

2171 return 0;

2181 1

2191

2201 // create a new world

2211 void BuildLifeO)

2221 f

2231 delete If:

2241 If = new Life;

2251 ChangeLife(;

2261 J

227

2281 // ---- display the world

2291 static void ShowWorld()

2301

2311 if (if !- 0)

2321 for (short y 0 0; y < HT; y++)

2331 for (short x = 0; x < WD; x++)

2341 if (lf->isPopulated(x, y))

2351 gui->WriteChar(Pop, x, y);

2361 1

2371

2381 // - change an existing world

2391 void ChangeLife()

2401 {

2411 short x 0 0, y = 0;

2421 unsigned char lastc = 0;

2431 unsigned char c = 0;

2441 ShowWorld();

245! gui->StatusLine

2461 ("up, down, ->, <-, Spacebar/Enter, Esc");

2471 while (c != ESC) {

Page 336: C++ Database Development 1558283579

Appendix C Software Listings 331

2481 gui->SetCursor(40, HT);

2491 cout << "x:" << setw(3) << x;

2501 cout << " y:" << setw(3) << y;

2511 cout.flush();

2521 gui->SetCursor(x, y);

2531 c = gui->GetKBCharo;

2541 switch (tolower(c))

2551 case ' ':

2561 case '\r':2571 // --- toggle a population

2581 if (!If->isPopulated(x,y)) f

259! lf->Populate(x,y);2601 gui->WriteChar(Pop, x, y);

261!2621 else {

2631 If->DePopulate(x,y);

2641 gui->WriteChar(' x, y);

26512661 gui->PutBack(lastc);

267! break;

2681 case UPARROW:

2691 // --- up cursor

2701 if (y-- . . 0)

2711 y = HT-1;

2721 lastc - c;

2731 break;

2741 case DOWNARROW:

2751 // --- down cursor

2761 if (++y -= HT)

2771 y = 0;2781 lastc = c;

2791 break;

2801 case LEFTARROW:

2811 // --- left cursor

2821 if (x-- -- 0)2831 x - WD-1;

2841 lastc - c;

2851 break;

Page 337: C++ Database Development 1558283579

332 c++ Database Development

2861 case RIGHTARROW:

2871 // --- right cursor

2881 if (++x == WD)

2891 x = 0;

2901 lastc = c;

2911 break;

2921 default:

2931 break;

2941 }

2951

2961 1

2971

2981 // delete the current life object

2991 void DeleteLifeo)

3001 t

3011 if (If !- 0 && If->ObjectExists())

3021 cout << "Current life object:

3031 << If->Name() << endl;

3041 if (gui->YesNo("Delete")) {

3051 If->DeleteObjecto;

3061 delete If;

3071 If = 0;

3081 1

3091 1

3101

3111

3121 ---- display names of life worlds

3131 void ListLives()

3141 t

3151 delete If;

3161 if - 0;

3171 cout << "Game of Life Objects" << endl;

3181 cout << .... << endl;

3191 Life lif;

3201 lif.FirstObjecto;

3211 while (lif.ObjectExistso) t

3221 cout << ".. . << lif.Name() << endl;

3231 lif.NextObjecto;

Page 338: C++ Database Development 1558283579

Appendix C Software Listings 333

3241 1

3251 gui->AnyKey();

3261 1

3271

3281 // display the world and wait

3291 void DisplayLifeO)

3301

3311 ShowWorld();

3321 gui->AnyKey();

3331 }

3341

3351 // ------- nonstop evolution

3361 void RunLife()

3371 f

3381 if (if != 0) 1

3391 ShowWorldo;

3401 while (If->isAliveo) {

3411 if (gui->KBCharWaitingo)

3421 char c = gui->GetKBChar();

3431 if (c == ESC)

3441 break;

3451

3461 lf->Generationo;

3471

34813491

3501

3511 // single-step evolution

3521 void StepLife()

3531 t

3541 if (if != 0) t

3551 ShowWorldo;

3561 gui->StatusLine("S-tep or Esc");

3571 short c;

3581 while ((c = gui->GetKBCharo) != ESC)

3591 if (tolower(c) -- 's')

3601 lf->Generationo;

3611

Page 339: C++ Database Development 1558283579

334 c++ Database Development

3621 1

3631

3641 // --------- retrieve a persistent Life object

3651 void ReadLife(const string& nm)

3661 {

3671 delete If;

3681 string name(nm);3691 name.resize(keylen);

3701 If = new Life(name):

3711 if (!If->ObjectExistso))

3721 gui->Error("Invalid name");

3731 else

3741 DisplayLifeo;

3751 }

3761

3771 const char *nameprompt = "Enter Life world name";

3781

3791 // --- retrieve persistent Life object from user input

3801 void LoadLife()

3811 {

3821 string nm;

3831 gui->Userlnput(&nm, nameprompt, keylen);

3841 ReadLife(nm);

3851 }3861

3871 // --------- store a persistent Life object

3881 void SaveLife()

3891 {

3901 if (if !- 0) f

3911 string svnm = lf->Nameo;

3921 if (If->ObjectExists())

3931 lf->ChangeObject();3941 else {3951 string nm(svnm);

3961 gui->Userlnput(&nm, nameprompt, keylen);

3971 If->SetName(nm);

3981 if (lf->AddObjecto))

3991 svnm - nm;4001 else {

Page 340: C++ Database Development 1558283579

Appendix C Software Listings 335

4011 If->SetName(svnm);

4021 gui->Error("Name already used");

4031 return;

4041

405

4061

4071 --- force output now

4081 If->SaveObjecto);

4091 gui->AnyKey();

4101

4111

LISTING C.21 PERSONEL.H

11 // ------------ personel.h

21

31 #ifndef PERSONEL H

41 #define PERSONEL H

51

61 typedef short int EmployeeNumber;

71 typedef short int DepartmentNumber;

81 typedef short int ProjectNumber;

91101 #include "parody.h"

111 #include "gui.h"121 #include "dept.h"

131 #include "employee.h"141 #include "project.h"

151 #include "assign.h"

161 #include "manager.h"

171181 extern GUI *gui;

191

201 #endif

LISTING C.22 PERSONEL.CPP

11 // personel.cpp

21

Page 341: C++ Database Development 1558283579

336 C++ Database Development

31 /-----------------------------------

41 / A Personnel Application

51 / using PARODY,61 / the Persistent, (Almost) Relational Object Database

71 / management system

81 /---------------------------

91101 #include "personel.h"

ill121 // Employee prototypes

131 void QueryEmployeeso);

141 void AddEmployeeo);

151 void ChangeEmployee();

161 void DeleteEmployeeo;171 void ListEmployees);

181 void ListEmployeeProjectso);

19201 // Department prototypes

211 void QueryDepartmentso);

221 void AddDepartment(;

231 void ChangeDepartmento;

241 void DeleteDepartmento;

251 void ListDepartmentso;

261 void ListDepartmentEmployeeso;

271 void ListAllDepartmentso);

281 void ListOneDepartment();291

301 // ----------- Project prototypes311 void QueryProjectso;

321 void AddProject();

331 void ChangeProjecto;

341 void DeleteProjecto;

351 void ListProjectso);361 void ListProjectEmployeeso);

371381 // Assignment prototypes

391 void QueryAssignmentso;401 void AddAssignment();

Page 342: C++ Database Development 1558283579

Appendix C: Software Listings 337

411 void Chan

421 void Dele

431 void List

441 void Post

45461 //------

471 void Quer481 void AddM

491 void List

501

51! /

521 / Person

531 / The Em

541 / classe

551 ------561

571 GUI *gui;

581

591 #include

601

611 int main(

621 t

631 gui -

641 Parod

651 /

661 t

671 E

681 D

691 P

701 A

711 M

721 J

731741 Scree

751761

771781

geAssignment();

teAssignment();

Assignments();

Assignments();

Manager prototypes

yManagersC);

anager();

Managers();

nel Databaseployee, Department, Project, and Assignment

s depend on this personnel object.

<mall oc.h>

new GUI;y *personnel = new Parody("PERSONEL");

- empty objects to declare relationships

mployee empl;

epartment dept;

roject proj;

ssignment assgn;

anager mgr;

nMenu( "Personnel Input"

"Employees".

"Departments",

"Projects",

"Assignments",

QueryEmployees,QueryDepartments,

QueryProjects,

QueryAssignments,

)

Page 343: C++ Database Development 1558283579

"Managers", QueryManagers,

NULL).Execute(;

gui->ClearScreeno);

delete personnel;

delete gui;

return 0;

//

II ------------ - - - - - -

// Employee processing

//------------

void QueryEmployees()

791

801

811

821

83

84

851

861

871

881

891

901

911

921931

941

951

961

971

981

991

1001

1011

1021

1031

1041

1051

1061

107

1081

1091

1101

1111

1121

1131

1141

1151

1161

AddEmployee

"Employees",

"Add", AddEmployee,

"Change", ChangeEmployee,

"Delete", DeleteEmployee,

"List", ListEmployees,"Projects", ListEmployeeProjects,

NULL).Execute(;

add employee objects

()

v

t

338 c++ Database Development

Employee *employee;

while ((employee = Employee::Geto) !0 0) 1

if (employee->ObjectExists()) I

Employee::Header(;

cout << *employee;

gui->Error("Employee already on file");}

else {

employee->Inputo;

if (!employee->AddObjecto)

gui->Error("Add disallowed");

ddelete employee;

ScreenMenu(

/oid

Page 344: C++ Database Development 1558283579

Appendix C: Software Listings 339

1171

1181

1191

1201 ---- --- change an existing employee object

1211 void ChangeEmployee()

1221 {

1231 Employee *employee = Employee::GetExisting();

1241 if (employee !0 0) {

1251 employee->SelectChangeo);

1261 delete employee;

1271 }

1281 1

1291

1301 //------------ delete an existing employee object

1311 void DeleteEmployee()

1321 {

1331 Employee *employee - Employee::GetExisting();

1341 if (employee != 0) f

1351 if (gui->YesNo("Delete the employee record"))

1361 if (!employee->DeleteObject()

1371 gui->Error("Delete disallowed");

1381 delete employee;

1391

1401 }1411

1421 -------- list all the employees

1431 void ListEmployees()

1441 {

1451 Employee empl;

1461 empl.FirstObjecto);

1471 Employee::Header(;

1481 while (empl.ObjectExistso))

149I cout << empl;

1501 empl.NextObjecto);

1511 1

1521 gui->AnyKey();

1531

1541

Page 345: C++ Database Development 1558283579

340 c++ Database Development

1551 // list the projects an employee is assigned to

1561 void ListEmployeeProjects()

1571 {

1581 Employee *employee = Employee::GetExisting(;

1591 if (employee !- 0) 0

1601 Project::Headero;

1611 Assignment assgn;

1621 assgn.SetEmplNo(employee->EmplNo();

1631 assgn.FindObject(&assgn.EmplNoKeyo);

1641 while (assgn.ObjectExists() &&

1651 assgn.EmplNo() == employee->EmplNo() )

1661 Project proj(assgn.ProjNo();

1671 if (proj.ObjectExists())

1681 cout << proj;

1691 assgn.NextObject(&assgn.EmplNoKey();

1701 }

1711 delete employee;

1721 }

1731 gui->AnyKey();

1741

1751

1761 //

1771 // Department processing

1781 //

1791 void QueryDepartments()

1801 {

1811 ScreenMenu( "Departments",

1821 "Add", AddDepartment,

1831 "Change". ChangeDepartment,

1841 "Delete", DeleteDepartment,

1851 "List", ListDepartments,

1861 "Employees", ListDepartmentEmployees,

1871 NULL).Execute();

1881 1

1891

1901 // add department objects

1911 void AddDepartment()

1921 {

Page 346: C++ Database Development 1558283579

Appendix C Software Listings 341

1931 Department *department;

1941 while ((department = Department::Get()) != 0)

1951 if (department->ObjectExists() t

1961 Department::Header();

1971 cout << *department;

1981 gui->Error("Department already on file");

1991 1

2001 else {

2011 department->Inputo;

2021 if (!department->AddObject()

2031 gui->Error("Add disallowed");

2041 1

2051 delete department;

2061 1

2071 1

2081

2091 // ------------ change a department object

2101 void ChangeDepartment()

2111 (

2121 Department *department = Department::GetExisting(;

2131 if (department != 0) (

2141 department->SelectChange();

2151 delete department;

2161 }

2171 1

2181

2191 // ------------ delete a department object

2201 void DeleteDepartment()

2211 f

2221 Department *department = Department::GetExisting();

2231 if (department != 0) t

2241 if (gui->YesNo("Delete the department record"))

2251 if (!department->DeleteObjecto)

2261 gui->Error("Delete disallowed");

2271 delete department;

2281 1

2291 1

2301

Page 347: C++ Database Development 1558283579

342 C++ Database Development

2311 // ----------- list the departments

2321 void ListDepartmentso)

2331 f

2341 Department dept;

2351 dept.FirstObject();2361 Department::Header();

2371 while (dept.ObjectExists())

2381 cout << dept;

2391 dept.NextObject();

2401 1

2411 gui->AnyKey(;

2421 1

2431

2441 // --------- list employees in departments

2451 void ListDepartmentEmployees(

2461 t

2471 ScreenMenu( "List Employees by Department",

2481 "All departments", ListAllDepartments,

2491 "One department", ListOneDepartment,

2501 NULL).Executeo;

2511 gui->AnyKeyo);

2521 1

2531

2541 // ------- list employees in all departments

2551 void ListAllDepartments()

2561 {

2571 Employee empl;

2581 Employee::Header();

2591 empl.FirstObject(empl.DeptKeyo);

2601 while (empl.ObjectExistso)) t

2611 cout << empl;

2621 empl.NextObject(empl.DeptKey();

2631

2641 gui->AnyKeyo;

2651 j

2661

2671 // ------- list employees in a selected department

2681 void ListOneDepartment()

Page 348: C++ Database Development 1558283579

Appendix C: Software Listings 343

2691 t2701 Department *department;

2711 while ((department=Department::GetExisting() 0= 0) f

2721 int deptno = department->DeptNo();

2731 Employee::Header()

2741 Employee empl;

2751 empl.SetDeptNo(deptno);

2761 empl.FindObject(empl.DeptKeyo);

2771 while (empl.ObjectExists() && empl.DeptNoo==deptno) t

2781 cout << empl;

2791 empl.NextObject(empl.DeptKey());

2801 1

2811 delete department;

2821 1

2831 gui->AnyKey);2841 1

2851

2861 //

2871 / Project processing

2881 //

2891 void QueryProjects()

2901

2911 ScreenMenu( "Projects",

2921 "Add", AddProject,

2931 "Change", ChangeProject,

2941 "Delete". DeleteProject,

2951 "List", ListProjects,

2961 "Employees", ListProjectEmployees,

2971 NULL).Execute( ;

2981

2991

3001 // a project objects

3011 void AddProject()

3021

3031 Project *project;

3041 while ((project = Project::Get)) != 0)

3051 if (project->ObjectExistso) t

3061 Project::Header();

Page 349: C++ Database Development 1558283579

344 C++ Database Development

3071

3081

3091

3101

3111

3121

3131

3141

3151

3161

3171

3181

3191

3201

3211

3221

3231

3241

3251

3261

3271

3281

3291

3301

331

3321

3331

3341

3351

3361

3371

3381

3391

3401

3411

3421

343I

3441

}

}

V{

/ --------------

oid ListProjects()

ist the project objects

Project proj;

cout << *project;gui->Error("Project already on file");

}

else fproject->Input();if (!project->AddObject())

gui->Error("Add disallowed");

delete project;

-------- change a project objectvoid ChangeProjecto){

Project *project - Project::GetExistingo);if (project !0 0) f

project->SelectChange();delete project;

}

}

-------- delete a project objectvoid DeleteProject(){

Project *project = Project::GetExistingo;if (project !- 0) {

if (gui->YesNo("Delete the project record"))if (!project->DeleteObject()

gui->Error("Delete disallowed");delete project;

Page 350: C++ Database Development 1558283579

Appendix C: Software Listings 345

3451 proj.FirstObject();

3461 Project::Header );

3471 while (proj.ObjectExists() {

3481 cout << proj;

3491 proj.NextObject();

3501 13511 gui->AnyKeyo;

3521 1

3531

3541 // list employees assigned to a project

3551 void ListProjectEmployees()

3561 {

3571 Project *project - Project::GetExistingo;

3581 if (project 1= 0) {

3591 Employee::Header( ;

3601 Assignment assgn;

3611 assgn.SetProjNo(project->ProjNoo);

3621 assgn.FindObject(&assgn.ProjNoKeyo);

3631 while (assgn.ObjectExists() &&

3641 assgn.ProjNo() == project->ProjNoo) {

3651 Employee empl(assgn.EmplNoo);

3661 if (empl.ObjectExists())

3671 cout << empl;

3681 assgn.NextObject(&assgn.ProjNoKeyo);

3691 1

3701 delete project;

3711 1

3721 gui->AnyKey();

3731 1

3741

3751

3761 / ----

3771 / Assignments

3781 / The Assignments class is a "connector" file

3791 -----------------------

3801 void QueryAssignments()

3811

3821 ScreenMenu( "Assignments",

Page 351: C++ Database Development 1558283579

346 C++ Database Development

3831 "Add", AddAssignment,

3841 "Change", ChangeAssignment,3851 "Delete", DeleteAssignment,

3861 "List", ListAssignments,

3871 "Post hours", PostAssignments,

3881 NULL).Execute();

3891 1

3901

3911 // --- -- add an assignment object

3921 void AddAssignment()

3931 {

3941 for C;;) {

3951 Assignment *assignment - Assignment::Geto;

3961 if (assignment == 0)

3971 break;3981 if (assignment->ObjectExistso) {

3991 Assignment::Headero;4001 assignment->DetailDisplayo;

4011 gui->Error("Assignment already on file");4021

4031 else t

4041 assignment->Inputo;

4051 assignment->AddObject();

4061

4071 delete assignment;

40814091 }

4101

4111 // change an assignment object

4121 void ChangeAssignment()

4131 {

4141 Assignment *assignment = Assignment::GetExisting();

4151 if (assignment != 0) {

4161 assignment->Input();

4171 delete assignment;

4181

4191 14201

Page 352: C++ Database Development 1558283579

Appendix C Software Listings 347

4211 // ----------- delete an assignment object

4221 void DeleteAssignment()

4231 {

4241 Assignment *assignment - Assignment::GetExisting();

4251 if (assignment !0 0) {

4261 if (gui->YesNo("Delete the assignment"))

4271 assignment->DeleteObjecto;

4281 delete assignment;

4291

4301

4311

4321 ----- post hours expended by the employee to the project

4331 void PostAssignmentso)

4341

4351 Assignment *assignment - Assignment::GetExisting);

4361 if (assignment !- 0) 0

4371 short int hours;

4381 gui->Userlnput(&hours, "Hours expended");

4391 if (hours !- 0) t

4401 assignment->AddHours(hours);

4411 Project project(assignment->ProjNo());

4421 if (project.ObjectExistso)

4431 project.AddHours(hours);

4441

4451 delete assignment;

4461

4471 1

4481

4491 // --------- list the assignment objects

4501 void ListAssignments()

4511 [

4521 Assignment assgn;

4531 assgn.FirstObjecto;

4541 Assignment::Header();

4551 while (assgn.ObjectExists())

4561 assgn.DetailDisplay();

4571 assgn.NextObject();

4581 }

Page 353: C++ Database Development 1558283579

348 c++ Database Development

4591 gui->AnyKey();

4601 1

461

4621 //

4631 / Managers

4641 //

46514661 void QueryManagers()

4671 f4681 ScreenMenu( "Manager Input",

4691 "Add", AddManager,

4701 "List", ListManagers,

4711 NULL).Execute();

4721 }4731

4741 // ---- add manager objects

4751 void AddManager()

4761 {

4771 Manager *manager;4781 while ((manager - Manager::Get() != 0)4791 if (manager->ObjectExists()

4801 gui->Error("Manager already on file");

4811 else t

4821 manager->Input();

4831 if (!manager->AddObjecto)4841 gui->Error("Add disallowed");

4851

4861 delete manager;

4871 1

4881 1

4894901 // ----- list manager objects

4911 void ListManagers()

4921 {

4931 Manager mgr;

4941 mgr.FirstObject();

4951 Employee::Header();4961 while (mgr.ObjectExistso))

Page 354: C++ Database Development 1558283579

Appendix C: Software Listings 349

4971 cout << mgr;

4981 mgr.NextObject(;4991 }

5001 gui->AnyKey();5011

LISTING C.23 EMPLOYEE.H

1 // ---------------- employee.h

21

31 #ifndef EMPLOYEE H

41 #define EMPLOYEE H51

61 #include "parody.h"

71 #include "dept.h"

8191 /

101 / Employee class11l / /--=--- - - - - -

121 class Employee : public Persistent f

131 Key<EmployeeNumber> emplno;

141 Key<DepartmentNumber> deptno;

151 string name;

161 protected:

171 virtual void Read();

181 virtual void Writeo;

191 public:

201 Employee(EmployeeNumber en = 0);

211 virtual -Employee(;221 EmployeeNumber EmplNo() const

231 t return emplno.KeyValue(); }

241 DepartmentNumber DeptNo() const251 { return deptno.KeyValue(); }

261 Key<DepartmentNumber> *DeptKey() t return &deptno; }

271 const string& Name() const281 f return name; )

291 void SetEmplNo(EmployeeNumber en);301 void SetName(const string& nm);

Page 355: C++ Database Development 1558283579

311

321

331

341

35136!

37138139!

40141142!431441

451

461

47148!491

501511521

53!

541

551561

57!

58!

59!

601611

LISTING C.24 EMPLOYEE.CPP

11 // --------------- employee.cpp21

31 #include <iomanip.h>41 #include "personel.h"

350 C++ Database Development

void SetDeptNo(DepartmentNumber dn);

// -.--- user interface functionsstatic void Header(ostream& os - cout);

static Employee *Get();

static Employee *GetExisting();void Inputo);

void InputName);

void InputDepartment();

void TryChangeo);void SelectChange();

friend ostream& operator<<(ostream&os. const Employee& em);

inline void Employee::SetEmplNo(EmployeeNumber en){

emplno.KeyValue() - en;

I

inline void Employee::SetName(const string& nm)

{name - nm;ChangeObject();

inline void Employee::SetDeptNo(DepartmentNumber dn)

deptno.KeyValueo) - dn;

ChangeObject();

I

#endif

Page 356: C++ Database Development 1558283579

Appendix C: Software Listings 351

Employee::Employee(EmployeeNumber en) : emplno(en), deptno(O){

deptno.Relate(&typeid(Department));

// --- if class not derived from Employee

if (en != -1)

LoadObject();

Employee::-Employee(

--- if class not derived from Employee

(emplno.KeyValue() != -1)

SaveObject();

/ read a persistent

oid Employee::Read()

EmployeeNumber emno;

DepartmentNumber dpno;

ReadObject(emno);

emplno.SetKeyValue(emno);

ReadObject(name);

ReadObject(dpno);

deptno.SetKeyValue(dpno);

/ - write a persistent

oid Employee::Write()

employee object

151

161171

181

191 1

201

211 /

221 v

231 {

241

251

261

271

281

291

301

311 1

321

33 /

34 v

351 {

WriteObject(emplno.KeyValue());

WriteObject(name);

WriteObject(deptno.KeyValueo);I

// display an employee object

ostream& operator<<(ostream&os, const Employee& em)

51

6171

8191

101111

121

131

141

IIif

employee object

361

371381

391401

411

421

Page 357: C++ Database Development 1558283579

352 c++ Database Development

431 t

441 os.width(5);

451 os << em.emplno.KeyValueo;

461 os.width(O);

47 os << s ';

481 os.setf(ios::]eft, ios::adjustfield);

491 os.width(15);

501 os << em.name.substr(O, 15);

511 os.width(O);

521 os.setf(ios::right, ios::adjustfield);

531 os << ';

541 os.width(5);

551 os << em.deptno.KeyValue() << endl;

561 os.width(O);

571 os.flush();

581 return os;

591 J

601

611 // ---- try to change an employee object

621 void Employee::TryChange()

631 {

641 if (!ChangeObject()

651 gui->Error("Change disallowed");

661 1

671

681 // -------- input employee name from user

691 void Employee::InputName()

701 t

711 gui->Userlnput(&name, "Name", 25);

721 if (ObjectExists()

731 TryChange();

741

751

761 // -------- input employee department from user

771 void Employee::InputDepartmento)

781 {

791 DepartmentNumber dept;

801 gui->Userlnput(&dept, "Department No");

Page 358: C++ Database Development 1558283579

811

821831

84851

861

871

881

891901

91192

931941

951961

971981

99I

1001101o

1021

10311041

1051

1061

1071

1081

1091

1101

1111

1121

1131

1141

1151

1161

1171

1181

Appendix C: Software Listings 353

deptno. SetKeyValue(dept);

if (ObjectExistso)

TryChange();

// employee input

void Employee::Input()

{

InputName();

InputDepartment();}

static Employee *This;

static void ChangeName()

t

This->InputName();

I

static void ChangeDept(){

This->InputDepartment(;}

// --- select an employee data member to change

void Employee::SelectChange(){

This = this;

OneLineMenu("N-ame, D-epartment",

ChangeName, ChangeDept).Execute(;}

// get an existing employee object

Employee *Employee::GetExisting({

Employee *employee = Get();

if (employee != 0) f

if (employee->ObjectExists() t

Page 359: C++ Database Development 1558283579

Header();

cout << *employee;

Ielse {

gui->Error("No such employee on file");

delete employee;

employee = 0;}

11911201

1211

1221

1231

1241

1251

1261

1271

1281

1291

1301

1311

1321

133

1341

1351

1361

1371

1381

1391

1401

1411

1421

1431

1441

1451

14611471

1481

1491

1501

1511

1521

os << endl;os << "Empl# Employee

os << "

os.flush(;

Dept#" << endl;----------- << endl;

LISTING C.25 DEPT.H

1i // -------------- dept.h21

354 C++ Database Development

Ireturn employee;

// --- get an employee object

Employee *Employee::Get()

EmployeeNumber emplno;

Employee *employee = 0;

gui->Userlnput(&emplno,

"Enter employee number (0 to ignore)");

if (emplno 0= 0)

employee = new Employee(emplno);

return employee;1

/ ---- display employee object header

void Employee::Header(ostream& os)

{

I

Page 360: C++ Database Development 1558283579

Appendix C Software Listings 355

31 #ifndef DEPARTMENTH

41 #define DEPARTMENTH

51

61 #include "parody.h"

71 #include "employee.h"

81

91 //

101 / Department class11l / /

121 class Department : public Persistent

131 Key<DepartmentNumber> deptno;

141 string name;

151 Key<EmployeeNumber> manager;

161 void Read();

171 void Write );

181 public:

191 Department(DepartmentNumber dn = 0);

201 -Department()

211 { SaveObject(); }

221 const string& Nameo) const

231 t return name; }

241 DepartmentNumber DeptNo() const

251 { return deptno.KeyValueo; }

261 void SetName(string& nm);

271 void SetManager(EmployeeNumber mgr);

281 // - user interface functions

291 void Inputo;

301 void InputName();

311 void InputManagero);

321 void TryChange(;

331 void SelectChange(;

341 static Department *Get();

351 static Department *GetExisting();

36! static void Header(ostream& os = cout);

371 friend ostream& operator<<(ostream&osconst Department& dp);

381 1;

391

401 inline void Department::SetName(string& nm)

411 {

Page 361: C++ Database Development 1558283579

356 c++ Database Development

421 name = nm;

431 ChangeObjecto;

441 }

451

461 inline void Department::SetManager(EmployeeNumber mgr)

471 {

481 manager.KeyValue() = mgr;

491 ChangeObjecto;

5O1 }

511

521 #endif

LISTING C.26 DEPT.CPP

11 // dept.cpp21

31 #include <iomanip.h>

41 #include "personel.h"

51

61 /

71 / Department class

81 /

91 Department::Department(DepartmentNumber dn)101 deptno(dn), manager(O)

iII {

121 manager.Relate(&typeid(Employee));

131 LoadObject();

141 1151

161 // read a persistent department object

171 void Department::Read()

181 1

191 DepartmentNumber dpno;

201 ReadObject(dpno);

211 deptno.SetKeyValue(dpno);

221 ReadObject(name);

231 EmployeeNumber mgr;

241 ReadObject(mgr);

Page 362: C++ Database Development 1558283579

Appendix C: Software Listings 357

251 manager.SetKeyValue(mgr);

261

271

281 ------- write a persistent department object

291 void Department::Write()

301 {

311 WriteObject(deptno.KeyValue();

321 WriteObject(name);

331 WriteObject(manager.KeyValue());

341 1

351

361 // ---- display department object header

371 void Department::Header(ostream& os)

381 {

391 os << endl;

401 os << "Dept# Department Mgr#" << endl;

4 11 o s < < .... . .. . .. . ... . .. .. .. . . . . .... < < en d l ;

421 os.flusho ;

431 1

441

451 // ---- display department object

461 ostream& operator<<(ostream&os,const Department& dp)

471 {

481 os.width(5);

491 os << dp.deptno.KeyValue(;

501 os.width(O);

511 os << ' ;

521 os.setf(ios::left, ios::adjustfield);531 os.width(15);

541 os << dp.name.substr(O, 15);

551 os.width(O);

561 os.setf(ios::right, ios::adjustfield);

571 os << K ;

581 os.width(5);

591 os << dp.manager.KeyValue();

601 os.width(O);

611 os << endl;

621 os.flush );

Page 363: C++ Database Development 1558283579

358 C++ Database Development

631 return os;

641 1

65

661 static Department *This;

671

681 static void ChangeNameo)

691 {

701 This->InputName);

711 1

721

731 static void ChangeMgr()74 {

751 This->InputManager();

761 )771

781 // ------ select department data member to change

791 void Department::SelectChange()801 {

811 This - this;

821 OneLineMenu("N-ame, M-anager",

831 ChangeName. ChangeMgr).Executeo;

841 }

851861 // --------- read department name from user

871 void Department::InputName()

881 f

891 gui->Userlnput(&name, "Name", 25);

901 if (ObjectExistso)

911 TryChange();

921 }

931

941 // --------- read department manager from user

951 void Department::InputManagero)

961 f

971 cout << "Manager:" << end];

981 cout.flush();

991 Employee *mgr = Employee::GetExistingo;1001 if (mgr !- 0) {

Page 364: C++ Database Development 1558283579

Appendix C: Software Listings 359

SetManager(mgr->EmplNo();

if (ObjectExists())

TryChange();

delete mgr;

I

101

102

103

104

105

106

1071

108

109

1101

1111

1121

11311141

1151

1161

1171

1181

1191

1201

1211

1221

1231

1241

1251

1261

1271

1281

1291

1301

1311

1321

1331

1341

1351

1361

1371

1381

v/

v

{

}

/

V

I

if (!ChangeObjecto)gui->Error("Change disallowed");

/ department o.

oid Department::Input()

bject user input

InputName(;InputManager();

// get an existing department object

Department *Department::GetExisting(

tDepartment *department = Get);if (department !0 0) 1

if (department->ObjectExists)) f

Header(;

cout << *department;}

else {gui->Error("No such department on file");

delete department;

department = 0;

return department;

/ ------- try to change the

oid Department::TryChange(

department object

I

Page 365: C++ Database Development 1558283579

360 c++ Database Development

1391

1401 // -------- get a department object

1411 Department *Department::Get()

1421 t

1431 DepartmentNumber deptno;

1441 Department *department = 0;

1451 gui->Userlnput(&deptno,

1461 "Enter department number (0 to ignore)");

1471 if (deptno !- 0)

1481 department = new Department(deptno);

1491 return department;

1501 J

LISTING C.27 PROJECT.H

11 // ------------- project.h21

31 #ifndef PROJECT H

41 #define PROJECT H

51

61 #include "parody.h"

71 #include "employee.h"

8191 II-------====--=== ------- ===- -

101 / Project class

121 class Project : public Persistent

131 Key<ProjectNumber> projno;

141 string name;151 Key<EmployeeNumber> manager;

161 short int hours-expended;

171 void Reado;181 void Writeo;

191 public:

201 Project(ProjectNumber prno = 0);

211 -Project()221 t SaveObjecto); }

231 const string& Nameo) const

Page 366: C++ Database Development 1558283579

Appendix C Software Listings 361

241 { return name; }251 ProjectNumber ProjNo() const

261 { return projno.KeyValue); J

271 short int Hours() const

281 t return hours-expended; }

291 void SetName(string& nm);

301 void SetManager(EmployeeNumber mgr):

311 void SetHours(short int hrs);

321 void AddHours(short int hrs);

331 // -------- user interface functions

341 void Input();

351 void InputNameo;361 void InputManagero);

371 void InputHourso;

381 void TryChangeo);

391 void SelectChange();401 static Project *Get();

411 static Project *GetExisting();

421 static void Header(ostream& os = cout);

431 friend ostream& operator<<(ostream&os, const Project& pr);

441 ;451

461 inline void Project::SetName(string& nm)

471 (

481 name = nm;

491 ChangeObjecto;

501 )511

521 inline void Project::SetManager(EmployeeNumber mgr)

531 {

541 manager.KeyValue() - mgr;

551 ChangeObjecto;561 )

571581 inline void Project::SetHours(short int hrs)

591 {

601 hours expended = hrs;

611 ChangeObject(;

Page 367: C++ Database Development 1558283579

362 c++ Database Development

621 1

631

641 inline void Project::AddHours(short int hrs)

651 {

661 hours-expended += hrs;

671 ChangeObject();

681 1

691

701 #endif

LISTING C.28 PROJECT.CPP

11 // project.cpp

21

31 #include <iomanip.h>

41 #include "personel.h"

51

61 /-

71 / Project class

81 /

91

101 Project::Project(ProjectNumber prno) : projno(prno), manager(O)

ill t

121 manager.Relate(&typeid(Employee));

131 hours expended = 0;

141 LoadObject();

151 1

161

171 // -------- read a persistent project object

181 void Project::Reado)

191 f

201 ProjectNumber prno;

211 ReadObject(prno);

221 projno.SetKeyValue(prno);

231 ReadObject(name);

241 EmployeeNumber mgr;

251 ReadObject(mgr);

261 manager.SetKeyValue(mgr);

Page 368: C++ Database Development 1558283579

Appendix C Software Listings 363

271 ReadObject(hours-expended);

281 1

291

301 // ----- write a persistent project object

311 void Project::Write()

321 f

331 WriteObject(projno.KeyValueo));

341 WriteObject(name);

351 WriteObject(manager.KeyValue());361 WriteObject(hours expended);

371 1381

391 // --------- display a project object header401 void Project::Header(ostream& os)

411 {

421 os << endl;

431 os << "Proj# Project Mgr# Hours" << endl;

441 os << ------- ------ << endl;

451 os.flusho);

461 1471481 // -- display a project object

491 ostream& operator<<(ostream&os, const Project& pr)501511 os.width(5);521 os << pr.projno.KeyValue();

531 os.width(O);

541 os << « ;

551 os.setf(ios::left, ios::adjustfield);561 os.width(15);

571 os << pr.name.substr(O. 15);581 os.setf(ios::right, ios::adjustfield);

591 os.width(O);601 os << « ;611 os.width(5);

621 os << pr.manager.KeyValueo;

631 os.width(O);

641 os << ' ;

Page 369: C++ Database Development 1558283579

651

661

671

681691

701711721

731

741

751

761

771781

791801

811821

831

841

851

861

871881

891

901911

921

931941

95196

97

98

9911001

1011

1021

}/V,

/ ------ select a project daoid Project::SelectChange(

ta member to change

This = this;

OneLineMenu("N-ame, M-anager, Hours",

ChangeName, ChangeMgr, ChangeHours).Execute(;}

-------- project object input

void Project::Input()

{

InputName();

InputManager();

364 c++ Database Development

os.width(5);

os << pr.hours-expended;

os.width(O);

os << endl;

os.flush();

return os;

static Project *This;

static void ChangeNameo{

This->InputName();}

static void ChangeMgr()

This->InputManager(;

static void ChangeHourso

TThis->InputHours();

Page 370: C++ Database Development 1558283579

Appendix C Software Listings 365

1031 InputHourso;

104 }

105

1061 ----- get the project number from the user

1071 void Project::InputName()

1081

109! gui->Userlnput(&name, "Name", 25);

1101 if (ObjectExists()

1111 TryChangeo;

1121

1131

1141 // ---- get the project manager from the user

1151 void Project::InputManager()

1161

1171 cout << "Manager:

1181 cout.flush( ;

119! Employee *employee - Employee::GetExisting();

1201 if (employee != 0) {

1211 SetManager(employee->Emp]No();

122! delete employee;

1231 if (ObjectExistso)

1241 TryChange();

1251

1261

1271

1281 ----- get the project hours expended from the user

129! void Project::InputHours()

130!

131! gui->Userlnput(&hours expended, "Hours expended");

132! if (ObjectExists()

1331 TryChange();

1341 1

1351

1361 // - try to change the project object

1371 void Project::TryChange()

1381 {

139! if (!ChangeObject())

140! gui->Error("Change disallowed");

Page 371: C++ Database Development 1558283579

366 C++ Database Development

1411

14211431 // get an existing project object

1441 Project *Project::GetExisting()

1451 {

1461 Project *project;

1471 if ((project = Get()) !- 0) 0

1481 if (project->ObjectExistso))

1491 Header();

1501 cout << *project;

1511

1521 else f1531 gui->Error("No such project on file");

1541 delete project;

1551 project = 0;

1561

1571 1

1581 return project;

1591 1

16011611 // ---------- get a project object

1621 Project *Project::Get()

1631

1641 ProjectNumber projno;

1651 Project *project = 0;

1661 gui->Userlnput(&projno,

1671 "Enter project number (0 to ignore)");

1681 if (projno != 0)

1691 project = new Project(projno);

1701 return project:

1711

LISTING C.29 ASSIGN.H

11 // -------- assign.h

21

31 #ifndef ASSIGN H

41 #define ASSIGN H

Page 372: C++ Database Development 1558283579

Appendix C Software Listings 367

5161/

71 / Assignment class

81 // employee/project assignment91 //

101 class Assignment : public Persistent

ill CatKey<EmployeeNumber,DepartmentNumber> assignment;121 short int hours-expended;

131 void Reado;

141 void Write( ;

151 public:

161 Assignment(EmployeeNumber en = 0, DepartmentNumber pn = 0);

171 -Assignment()181 t SaveObject ); I

191 Key<EmployeeNumber>& EmplNoKey()

201 f return assignment.Keyl(); }

211 Key<ProjectNumber>& ProjNoKeyo)

221 { return assignment.Key2(); }

231 EmployeeNumber& EmplNo()

241 ( return assignment.KeyValuel(); }

251 ProjectNumber& ProjNo()

261 f return assignment.KeyValue2(); }

271 void SetEmplNo(EmployeeNumber en)

281 { assignment.SetKeyValuel(en); }

291 void SetProjNo(ProjectNumber pn)

301 t assignment.SetKeyValue2(pn); }

311 short int Hours() const

321 t return hours expended; }

331 void SetHours(short int hrs);

341 void AddHours(short int hrs);

351 // -- user interface functions361 void DetailDisplay(ostream& os = cout);

371 void Input();381 static Assignment *Get();

391 static Assignment *GetExisting();401 static void Header(ostream& os = cout);

411 friend ostream& operator<<(ostream&os,const Assignment& as);

421 1;

Page 373: C++ Database Development 1558283579

368 C++ Database Development

43

441 inline void Assignment::SetHours(short int hrs)

451 {

461 hours expended - hrs;

471 ChangeObject(;

481 }

491 inline void Assignment::AddHours(short int hrs)

501 {

511 hours-expended += hrs;

521 ChangeObjecto;

531 }

541

551 #endif

LISTING C.30 ASSIGN.CPP

11 // assign.cpp

21

31 #include <iomanip.h>

41 #include "personel.h"

5161 // ---- construct from employee number, project number

71 Assignment::Assignment(EmployeeNumber en, ProjectNumber pn)81 assignment(en, pn)

9I t101 EmplNoKeyo.Relate(&typeid(Employee));

ill ProjNoKey(.Relate(&typeid(Project));

121 hours expended - 0;

131 LoadObjecto);

141

151

161 --------- read the persistent assignment object

171 void Assignment::Read()

181 {191 ReadObject(EmplNoo));

201 ReadObject(ProjNoo);

211 ReadObject(hours expended);

221 }

Page 374: C++ Database Development 1558283579

Appendix C: Software Listings 369

231

241 // -- write the persistent assignment object

251 void Assignment::Write()

261 {271 WriteObject(EmpINoo);

281 WriteObject(ProjNo();

291 WriteObject(hours-expended);

301

311321 //- display an assignment object header

331 void Assignment::Header(ostream& os)

34 {

351 os << endl;

361 os << "Empl# Employee Proj#

371 "Project Hours" << endl;

381 os << - -

39 1 .. -------------- - - -. << end l:

401 os.flusho ;

411 1

421

431 // -- display an assignment object

441 ostream& operator<<(ostream&os,const Assignment& as)

451 {461 os << "Empl #:

471 << setw(5)481 << (const-cast<Assignment&>(as)).EmpINo()

491 << setw(O);501 os << " Proj #:

511 << setw(5)

521 << (const cast<Assignment&>(as)).ProjNo()

531 << setw(O);

541 os << " Hours:

551 << setw(5)

561 << as.hours expended

571 << setw(O)581 << endl;

591 os.flush();601 return os;

Page 375: C++ Database Development 1558283579

370 C++ Database Development

611 1621

631 // -- display an assignment object's details

641 void Assignment::DetailDisplay(ostream& os)

651 {

661 Employee empl(EmplNoo);

671 Project proj(ProjNo());

681

691 os << setw(5) << empl.EmplNo();

701 os << setw(O);

711 os << « ;721 os.setf(ios::left, ios::adjustfield);

731 os << setw(15) << empl.Name().substr(O, 15);

741 os.setf(ios::right, ios::adjustfield);

751 os << setw(O);

761 os << « ;771 os << setw(5) << proj.ProjNoo;

781 os << setw(O);

791 os << ( ;801 os.setf(ios::left, ios::adjustfield);811 os << setw(15) << proj.Name(.substr(O, 15);

821 os.setf(ios::right, ios::adjustfield);

831 os << setw(O);

841 os << ';851 os << setw(5) << hours-expended << endl;

861 os << setw(O);

871 os.flush();

881 1891

901 // --- get an existing assignment object911 Assignment *Assignment::GetExistingo)

921 t

931 Assignment *assignment;

941 if ((assignment - Get()) !0 ) t

951 if (assignment->ObjectExists() t

961 Headero;

971 assignment->Detai]Display(;981 1

Page 376: C++ Database Development 1558283579

else t

gui->Error("No such assignment on file");

delete assignment;

assignment = 0;I

99

100

101l

102

1031

1041

1051

106

107

1081

109!

1101

111i112!

1131

1141

1151

1161

1171

11811191

1201

1211

1221

1231

1241

1251

1261

1271

1281

1291

1301

1311

LISTING C.31 MANAGER

11 II21

-- manager.h

Appendix C: Software Listings 371

return assignment;

--- get an assignment object

Assignment *Assignment::Get(){

Assignment *assignment = 0;

Employee *employee = Employee::GetExisting(;

if (employee != 0) {

Project *project - Project::GetExistingo;

if (project != 0) 1

assignment = new Assignment(employee->EmplNo(),

project->ProjNo());

delete project;}

delete employee;}

return assignment;

// assignment object input

void Assignment::Input(){

gui->Userlnput(&hours expended, "Hours expended");

if (ObjectExists())

ChangeObject();

Page 377: C++ Database Development 1558283579

372 C++ Database Development

31 #ifndef MANAGER H

41 #define MANAGERH51

61 #include "parody.h"

71 #include "employee.h"

819 //

101 / Manager class

121 class Manager : public Employee {

131 Money bonus;

141 void Read();

151 void Write();

161 public:

171 Manager(EmployeeNumber en = 0);

181 -Managero;

191 // user interface functions

201 static Manager *Get();

211 void Input();

221 friend ostream& operator<<(ostream&os, const Manager& mgr);

231 };

241

251 #endif

LISTING C.32 MANAGER.CPP

11 // manager.cpp

21

31 #include "personel.h"

41

51 Manager::Manager(EmployeeNumber en) : Employee(-1)

61 {

71 SetEmplNo(en);

81 LoadObjecto;

91 )

101

111 Manager::-Manager()

121 {

Page 378: C++ Database Development 1558283579

Appendix C Software Listings 373

131 SaveObject(;

141 SetEmp]No(-1);

151

161171 void Manager::Read()

181 (

191 Employee::Read();

201 ReadObject(bonus);

211 1

221

231 void Manager::Write()

241 {

251 Employee::Write();

261 WriteObject(bonus);

271 1

281

291 // get an employee object

301 Manager *Manager::Get()

311 {

321 EmployeeNumber emplno;

331 Manager *manager = 0;341 gui->Userlnput(&emplno,

351 "Enter manager's employee number (0 to ignore)");361 if (emplno 0= 0)

371 manager - new Manager(emplno);381 return manager;

391 1

401

411 void Manager::Input()

421 f

431 Employee::Input(;

441 gui->Userlnput(&bonus, "Bonus");

451 )

461

471 // display a manager object481 ostream& operator<<(ostream&os, const Manager& mgr)

491 s501 os << static-cast<const Employee&>(mgr)

Page 379: C++ Database Development 1558283579

374 c++ Database Development

511 << ".... Bonus: " << mgr.bonus << endl;

521 os.flusho ;

531 return os;

541 J

LISTING C.33 PROJVIEW.CPP

1i //-21

31 I-------41 / View tw

5 1 --- --61

71 #include

81

91 GUI *gui;

101

11 int main()

121 t

131 gui =

141 Parody

151

161 Projec

171

181 try

191 f

201 pr

211 pr

221 1

231 catch(

241 t

251 gu

261 pr

271 1

281

291 Projec

301 if (pr

311 co

projview.cpp

o projects stored in the PERSONEL database

personel.h"

new GUI;*personnel = new

t *projl, *proj2;

Parody("PERSONEL");

ojl - Project::GetC);

oj2 - Project::GetC);

Persistent *obj)

i->Error("You are viewing the same project twice");

oj2 - static cast<Project*>(obj);

t::Header();

ojl->ObjectExists())

ut << *projl;

Page 380: C++ Database Development 1558283579

Appendix C[ Software Listings 315

321 if (proj2->ObjectExistso))

331 cout << *proj2;

34135) Persistent::Destroy(projl):

361 Persistent::Destroy(proj2);

371

381 gui->AnyKeyo;

39

401 delete personnel;411 delete gui;

421

431 return 0;

44) J

LISTING C.34 INDEX.CPP

2)

3) /

4) / A prog

51 / for a

7) #include

8) #include

10)

11i

12)

13)

14)

15)

16)

17)

18)

19)

20)

21)

22)

index.cpp

ram to rebuild the indexes

specific PARODY database

<stdio.h>"personel.h" // application-specific header

#define dbname "PERSONEL"

GUI *gui;

void Buildlndex()

{remove(dbname ".ndx");

Parody *personnel = new

ObjectHeader objhdr;

// delete old index file

Parody(dbname);

/-/ - empty objects to declare relationships{

Employee empl;

Department dept;

Page 381: C++ Database Development 1558283579

376 C++ Database Development

231 Project proj;

241 Assignment assgn;

251

26

271 const ClassID EMPLOYEE =

281 personnel->GetClasslD(typeid(Employee).name());

291 const ClassID DEPARTMENT =

301 personnel->GetClasslD(typeid(Department).nameo);

311 const ClassID PROJECT =

321 personnel->GetC]asslD(typeid(Project).name());

331 const ClassID ASSIGNMENT =

341 personnel->GetC]asslD(typeid(Assignment).name();

351

361 NodeNbr nd 1 1;

371 NodeNbr end personnel->datafile.HighestNode();

381 // ---- scan Parody nodes

391 while (nd <= end) {

401 // --- read the object header for this node

411 personnel->GetObjectHeader(nd, objhdr);

421 // --- object relative node# 0 is 1st node of object

431 if (objhdr.ndnbr =- 0) {

44 // --- tell Parody to rebuild indexes this object

451 personnel->Rebuildlndexes(nd);

461 // ------ rebuild depending on class type

471 if (objhdr.classid - EMPLOYEE) {

481 Employee empl;

491 cout << "Empl: << empl;

501 1511 else if (objhdr.classid == DEPARTMENT) {

521 Department dept;

531 cout << "Dept: " << dept;

541 }

55 else if (objhdr.classid == PROJECT) I

561 Project proj;

571 cout << "Proj: " << proj;

581 1

591 else if (objhdr.classid == ASSIGNMENT)

601 Assignment assgn;

Page 382: C++ Database Development 1558283579

Appendix C: Software Listings 377

611

621

631

641 nd-

651 1661 delete

671 1

681

691 int main()

701 {

711 Buildl

721 return

731

cout << "Asgn: " << assgn;

personnel;

index )

0;

LISTING C.35 FAMILY.H

11

2131

4151

61

71

81

91

101

11i

121

131

141

151

161

171

181

191

201

211

221

// --- family.h

#include "parody.h"

#include "date.h"

enum Sex { unknown, male, female };

const int namelen = 18;

class FamilyMember : public Persistent

Key<string> name;

Sex sex;Date birthdate;

Date deathdate;Reference<FamilyMember> mother;

Reference<FamilyMember> father;

void Read();void Writeo;

public:FamilyMember(const string& nm = string('\O', namelen));

FamilyMember(ObjAddr oa) : name("")

{ LoadObject(oa); I

-FamilyMember()

I

Page 383: C++ Database Development 1558283579

378 C++ Database Development

231 f SaveObjecto);

241 string& Nameo)

251 f return name.KeyValue(; }

261 void SetName(const string& nm)

271 t name.SetKeyValue(nm); }

281 Sex GetSex()

291 [ return sex; }

301 void SetSex(Sex sx);

311 bool isMaleo)

321 { return sex =- male; }

331 bool isFemaleo)

341 { return sex =- female; }

351 const Date& BirthDate() const

361 { return birthdate: }

371 const Date& DeathDateo) const

381 return deathdate: }

391 void SetBirthDate(const Date& dt)

401 { birthdate = dt; }

411 void SetDeathDate(const Date& dt)

421 t deathdate = dt; }

431 void SetMother(FamilyMember *mth)

441 t if (mth->sex -= female) mother = *mth; I

451 void SetFather(FamilyMember *fth)

461 t if (fth->sex == male) father = *fth; }

471 static void Header(ostream& os = cout);

481 friend ostream&operator<<(ostream&os,FamilyMember&f);

491 1;

LISTING C.36 FAMILY.CPP

i // ------ family.cpp

21

31 #include "family.h"

41

5 1 / /

61 / FamilyMember class member functions

71 1/

81

91 FamilyMember::FamilyMember(const string& nm) name(nm)

Page 384: C++ Database Development 1558283579

Appendix C: Software Listings 379

101ill121

131

141151

161

171

181

191

201

211

221

231

241

251

261

271

281

291

301

311

321

331

341

351

361

371

381

391

401

411

421

43I

44I

451

461

471

481

os << "Name

"Mother

os << - --

Sex Born Died

Father" << endl;

" << end];

static void DisplayName(ostream& os, FamilyMember *mbr)

tos.width(namelen+l);

os.setf(ios::left);

sex = static cast<Sex>(O);

LoadObject();

void FamilyMember::Read({

string nm;

ReadObject(nm);

name.SetKeyValue(nm);

ReadObject(sex);ReadObject(birthdate);

ReadObject(deathdate);

mother. ReadObject();

father.ReadObject();

void FamilyMember::Write(

WriteObject(name.KeyValue());

WriteObject(sex);

WriteObject(birthdate);

WriteObject(deathdate);

mother.WriteObject()o;

father.WriteObject();

I

void FamilyMember::Header(ostream& os)I

Page 385: C++ Database Development 1558283579

380 C++ Database Development

491 if (mbr == 0)501 os <<

511 else

521 os << mbr->Nameo;

531 1

541

551 ostream& operator<<(ostream& os.Fami]yMember& fm)

56 {

571 os.width(namelen);

581 os.setf(ios::left);

591 os << fm.Name(;601 os << ' '

611 if (fm.isMaleo)

621 cout << 'M';

631 else if (fm.isFemale())

641 cout << 'F';

651 else

661 cout <<671 os << ( . . ;

681 os << fm.birthdate;

691 os << ';

701 os << fm.deathdate;

711 os << ' ';

721 DisplayName(os, fm.mother.obj);

731 DisplayName(os, fm.father.obj);741 os << endl;

751 return os;

761 3

771781 void FamilyMember::SetSex(Sex sx)

791 {

801 if (sx unknown II sx == male sx female)

811 sex = sx;

821 1

LISTING C.37 FAMTREE.CPP

11 // - famtree.cpp

21

Page 386: C++ Database Development 1558283579

#include "family.h"

#include "gui.h"

Parody *ftree;

GUI *gui;

static FamilyMember *fm;

static FamilyMember *fml;

static short linect = 0;

static short selected;

static bool quitting;

31

41

51

6171

81

91

10

11

121

13

14

15

161

171

181

191

201

211

221

231

241

251

261

271

AddRecord();

ListRecords();

ViewRecord();

ChangeSex();

ChangeBirthDate();

ChangeDeathDate);

ChangeRcd();

DeleteRcd();

ChangeName();

ChangeParent();

ChangeDates(;

ChangeMother();

ChangeFather();

Marriages();

281 static void SelectMember();

static FamilyMember *GetListedMember(Sex sx = unknown);

int main()

t

gui = new GUI;

ftree = new Parody("FAMILY");

ScreenMenu("Family Tree",

"Add family members",

"List family members",

"View a family member",

NULL).Execute();

gui->ClearScreen(;

AddRecord,

ListRecords,

ViewRecord,

Appendix C: Software Listings 381

void

void

void

void

void

void

void

void

void

voidvoid

void

void

void

static

static

static

static

static

static

static

static

static

static

static

static

static

static

291301

311321

331

341

351361

371381

391401411

Page 387: C++ Database Development 1558283579

del ete

delete

return

ftree;

gui

0;

I

421

431

441

451

461

471

481

491501511521

531541

551561

571581

591601

611

621

631

641

651661

671

681

691

701711721

731

741

751761

771781

791

382 c++ Database Development

static void More(){

--- intentionally empty

static void Quito

Iquitting - true;

}

static void ReadName(string& nm)

gui->Userlnput(&nm, "Name", 80);

nm.resize(namelen);

static FamilyMember *GetExistingMember(

{FamilyMember *fmbr;

string name;

ReadName(name);

try t

fmbr - new FamilyMember(name);

if (!fmbr->ObjectExistso) t

Persistent::Destroy(fmbr);

fmbr = 0;}

catch (Persistent *obj) t

fmbr = static cast<FamilyMember*>(obj);

rreturn fmbr;

Page 388: C++ Database Development 1558283579

Appendix C Software Listings 383

801

811 static void AddRecord()

821 (

831 string name;

841 ReadName(name);

851 fm = new FamilyMember(name);

861 if (fm->ObjectExists())

871 gui->Error("Name is already used");

881 else t

891 ChangeSex();

901 ChangeBirthDateo;

911 ChangeDeathDateo;

921 FamilyMember::Headero;

931 cout << *fm;

941 if (gui->YesNo("Add record"))

951 if (!fm->AddObjecto)

961 gui->Error("Cannot add");

971 }

981 Persistent::Destroy(fm);

991 fm = 0;1001 }

1011

1021 static void ListRecords()

1031 {

1041 FamilyMember::Header();

1051 FamilyMember fm;

1061 fm.FirstObjecto;

1071 while (fm.ObjectExists())

1081 cout << fm;

1091 fm.NextObject);

1101

111 gui->AnyKey();

1121 1

1131

1141 static void ViewRecord()

1151

1161 if ((fm = GetExistingMember()) == 0)

1171 fm = GetListedMember();

Page 389: C++ Database Development 1558283579

384 C++ Database Development

1181 if (fm != 0) 1

1191 FamilyMember::Header();

1201 cout << *fm;

1211 OneLineMenu("C-hange, D-elete, Q-uit",

1221 ChangeRcd, DeleteRcd, Quit).Execute();

1231 Persistent::Destroy(fm);

1241 fm = 0;

1251

1261

1271

1281 static void ChangeRcd()

1291 {

1301 OneLineMenu("N-ame, S-ex, D-ates, P-arent, 0-uit",

1311 ChangeName,

1321 ChangeSex,

1331 ChangeDates,

1341 ChangeParent,

1351 Quit).Execute();

1361 if (!fm->ChangeObjecto)

1371 gui->Error("Cannot change");

1381 else {

1391 FamilyMember::Header(;

1401 cout << *fm;

1411 gui->AnyKey();

1421 1

1431 }

1441

1451 static void ChangeName()

1461 {

1471 string name;

1481 ReadName(name);

1491 fm->SetName(name);

1501 11511

1521 static void ChangeSex()

1531 {

1541 char sex - 0;

1551 while (sex 1= 'i' && sex 1= 'f')

Page 390: C++ Database Development 1558283579

1561

1571

1581

15911601

1611

1621

1631

1641

1651

1661

16711681

1691

1701171117211731174117511761

17711781

1791

1801

1811

1821

1831

1841

1851

1861

1871

1881

1891

1901

1911

1921

1931

}S

{

}

gui->Userlnput(&dt, "Date

fm->SetBirthDate(dt);

tatic void ChangeDeathDate()

Date dt;

gui->Userlnput(&dt, "Date

fm->SetDeathDate(dt);

of birth");

of death");

static void ChangeParent()

{OneLineMenu("M-other, F-ather, 0-uit",

ChangeMother,ChangeFather,Ouit).Execute();

I

static void SelectMember()

tshort seln;

gui->Userlnput(&seln, "Enter member #");

Appendix C: Software Listings 385

gui->Userlnput(&sex, "Sex");

if (sex -- ESC)

return;

cout << '\b' << sex;

fm->SetSex(sex == m ? male : female);

static void ChangeDates()

{OneLineMenu("B-irth, D-eath, 0-uit",

ChangeBirthDate,ChangeDeathDate,Quit).Execute(;

I

static void ChangeBirthDate()

{Date dt;

Page 391: C++ Database Development 1558283579

1941

1951

1961

1971

1981

1991

2001

2011

2021

2031

2041

2051

206

2071

2081 1

209

2101 state

2111

2121

2131

2141

2151

2161

2171

2181

2191

2201

2211

2221

2231

2241

2251

2261

2271

2281

2291

2301

231 fml->NextObjecto);

386 C++ Database Development

if (seln < 1 11 seln > linect)gui->Error("Invalid selection");

else I

while (seln++ < linect+1) {

try {

fml->PreviousObject();}

catch (Persistent* obj) {

Persistent::Destroy(obj);

fml->PreviousObjecto);

selected = true;

tic FamilyMember *GetListedMember(Sex sx)

gui->ClearScreen();

fml = new FamilyMember;

try {fml->CurrentObject(;

}

catch (Persistent* obj) f

Persistent::Destroy(obj);

fml->NextObject()I

linect = 0;selected = 0;quitting = false;

while (fml->ObjectExists()) f

if (sx == unknown sx == fml->GetSexo))

cout.width(5);

cout << ++linect << ": " << fml->Name() << endl;

try {

Page 392: C++ Database Development 1558283579

Appendix C Software Listings 387

2321

2331 catch (Persistent* obj) {

2341 Persistent::Destroy(obj);

2351 fml->NextObjecto);

2361 1

2371 if (linect -- SCREENHEIGHT-3 I1 !fml->ObjectExists())

2381 OneLineMenu("M-ore, S-elect, 0-uit",2391 More,SelectMember,Quit).Executeo;

2401 if (quitting 11 selected 0)

2411 break;

2421

2431

2441 if (!selected) {

2451 Persistent::Destroy(fml);

2461 fml = 0;2471 1

2481 return fml;

2491 1

2501

2511 static void ChangeMother()

2521 {

2531 FamilyMember *mth - 0;

2541 if ((mth = GetExistingMember()) == 0)2551 mth - GetListedMember(;

2561 if (mth 0= 0) t

2571 if (mth -= fm)

2581 gui->Error("Cannot be one's own mother");2591 else if (mth->GetSex() !- female)

2601 gui->Error("Mother must be female");

2611 else

2621 fm->SetMother(mth);

2631 Persistent::Destroy(mth);

2641 1

2651 12661

2671 static void ChangeFather()

2681

2691 FamilyMember *fth = 0;

Page 393: C++ Database Development 1558283579

388 C++ Database Development

2701 if ((fth = GetExistingMember()) == 0)

2711 fth = GetListedMember(;

2721 if (fth 0) t

2731 if (fth == fm)

2741 gui->Error("Cannot be one's own father");

2751 else if (fth->GetSex() != male)

2761 gui->Error("Father must be male");

2771 else

2781 fm->SetFather(fth);

2791 Persistent::Destroy(fth);

2801

2811 1

2821

2831 static void DeleteRcd()

2841 {

2851 if (gui->YesNo("Delete family member"))

2861 if (!fm->Delete0bjectO)

2871 gui->Error("Cannot delete");

2881 }

Page 394: C++ Database Development 1558283579

Glossary

This glossary defines terms that have specific meaning in the realms ofC++, object-oriented programming, and database management. To helpyou separate the meanings, the definitions are prefixed with (OOP) forobject-oriented programming terms, (C++) for terms that apply specifi-cally to the C++ language, and (DB) for terms related to databases anddatabase management.

abstract base class-(OOP) A class definition that will always be a baseclass for other classes to be derived from. No specific objects of the baseclass will be declared by the program. A C++ abstract base class is onethat has a pure virtual function, a protected constructor, or a protecteddestructor.

abstract data type-(OOP) Also called "ADT" A user-defined data typebuilt as a C++ class. The details of implementation are not necessarily apart of the ADT. See also "primitive data type" and "concrete data type."

abstraction-(OOP) Defining an abstract data type by designing a class.

anonymous object-(C++) An internal, temporary object created by thecompiler.

argument-(C++) The value passed to a function. Its type must matchthat of the function's corresponding parameter as declared in the func-tion's prototype. See also "parameter."

389

Page 395: C++ Database Development 1558283579

390 C++ Database Development

base class-(OOP) A class from which other classes derive characteris-tics. All the characteristics of the base class are inherited by the derivedclass. Also called "superclass."

catalog-(DB) The file of database format and rules statements that con-stitute the definition of the database schema.

child record-(DB) A record in a database that is related to higherrecords in the database. The higher records are parent records.

class-(OOP) A user-defined data type that may consist of data members

and member functions.

class hierarchy-(OOP) A system of base and derived classes.

column-(DB) A data element field in a relational database table of rowsand columns. A column represents all the instances of the same data ele-ment in the file/table.

concrete data type-A user-defined or library data type complete withinterface and implementation. The CDT is meant to be instantiated as anobject and is not intended solely to be derived from.

constructor-(C++) The function executed by the compiler when the pro-gram declares an instance of a class. See also "destructor."

data definition language-(DB) The language that describes the schemafor a database.

data element-(DB) A single entity of data, usually one item of a datatype. Collections of data elements form files in a database.

data manipulation language-(DB) The language that application pro-grams use to store, retrieve, and maintain data in a database. Sometimesreferred to by the less-specific term "application program interface."

data member-(C++) A data component of a class. It may be any validdata type, including class objects and references.

database-(DB) A collection of data files loosely integrated to support acommon application.

Page 396: C++ Database Development 1558283579

Glossary 391

database management system-(DB) General-purpose systems softwarethat manages the storage and retrieval of data in a database.

declaration-(C++) As opposed to "definition." A declaration is the state-ment that defines the format of a type. A declaration reserves no memory.

definition-(C++) As opposed to "declaration." A definition is thestatement that declares the existence of an object. A definition reservesmemory.

derived class-(OOP) A class that inherits some of its characteristics froma base class. Also called a "subclass."

destructor-(C++) The function executed by the compiler when adeclared instance of a class goes out of scope. See also "constructor."

domain-(DB) A column in a relational database that adheres to speci-fied rules in the schema with respect to content and format. Similar to a"data element" except that the catalog rules restrict the allowable datavalues.

encapsulation-(OOP) The activity of defining a class with its data mem-bers and member functions encapsulated into the definition.Encapsulation implies an implementation, which is hidden from the classuser, and an interface, which is visible to the class user.

extraction operator-(C++) The overloaded >> operator that reads(extracts) values from an input stream. See also "insertion operator."

file-(DB) A collection of records of a common format in a database.

free store-(C++) The C++ heap. A dynamic memory pool that programsuse to allocate and release temporary memory buffers.

friend-(C++) A function that has access to the private members of aclass but that is not a member function of that class. The class definitiondeclares the function to be a friend.

hierarchical data model-(DB) A database of parent and child files. Eachrecord in a parent file may have multiple child records. Each record in achild file may have only one parent record. A child file may be parent to

Page 397: C++ Database Development 1558283579

392 C++ Database Development

lower child files, forming a multiple-level hierarchy. See also "networkdata model" and "relational data model."

hierarchy-G(OOP) See "class hierarchy."

inheritance-(OOP) The ability for one class to inherit the characteristicsof another. The inherited class is said to be derived from the base class.Also called "subclassing."

implementation-(OOP) The private members of a class. The implemen-tation defines the details of how the class implements the behavior of theabstract base type. See also "interface."

index-(DB) The file that associates key data values with the records thatthey index.

inline function-(C++) A function that the compiler compiles as inlinecode every time the function is called.

insertion operator-(C++) The overloaded << operator that writes

(inserts) values to an output stream. See also "extraction operator."

instantiate-(OOP) Declare an object of a data type, usually a class.

interface-(OOP) The public members of a class, which define the classuser's interface to the class's data and its behavior. Usually implementedas member functions. See also "implementation."

intrinsic data type-(OOP) See "primitive data type."

join-(DB) A relational database operator that creates a new table bylogically joining two other tables on the basis of common values in acommon column.

key-(DB) A data element used to index a record in a database.

linkage specification-(C++) Notation that tells the C++ compiler that afunction was or is to be compiled with the linkage conventions of anoth-er language.

manipulator-(C++) A value that a program sends to a stream to tell thestream to modify one of its modes.

Page 398: C++ Database Development 1558283579

Glossary 393

member-(C++) A component of a class, either a data member or a mem-ber function.

member function-(C++) A function component of a class, also called a"method." A member function may be virtual.

message-(OOP) A message is the invocation of a class's member func-tion in the name of a declared object of the class. The message is said tobe sent to the object to tell it to perform its function. The messageincludes the function call and the arguments that accompany it.

method-(OOP) A method in C++ is a member function of a class.Programs send messages to objects by invoking methods.

multiple inheritance-(OOP) The ability for a derived class to inherit thecharacteristics of more than one base class.

network data model-(DB) A database of parent and child files. Eachrecord in a parent file may have multiple child records. Each record in achild file may have multiple parent records. A child file may be a parentto lower child files, forming a multiple-level network. See also "hierarchi-cal data model" and "relational data model."

object-(OOP) A declared instance of a data type including standard C++

data types as well as objects of classes.

object database-(OOP) A collection of persistent objects.

overloaded function-(C++) A function that has the same name as one ormore other functions but that has a different parameter list. The compilerselects which function to call based on the data types and number ofarguments in the call.

overloaded operator-(C++) A function that executes when a C++ opera-tor is seen in a defined context with respect to a class object.

overriding function-(C++) A function in a derived class that has the samename, return type, and parameter list as a function in the base class. The com-piler calls the overriding function when the program calls that function in thename of an object of the derived class. If the function in the base class is "virtu-al," the compiler calls the derived class's function even when the call is througha pointer or reference to the base class. See also "pure virtual function."

Page 399: C++ Database Development 1558283579

394 C++ Database Development

parameter-(C++) The declaration of a data item that a function expectsto be passed to it. This declaration includes the item's type and name andappears in the function's declaration block at the beginning of the func-tion. When the parameter appears in the function's prototype, the para-meter's name may be omitted. See "argument" and "prototype."

parameter list-(C++) The list of parameter types and names in a func-tion declaration block. Also the same list, which may exclude the names,in a function prototype.

parent record-(DB) A record in a database that is related to lowerrecords in the database. The lower records are "child" records.persistence-(OOP) The ability of an object to succeed its creator and to

subsequently exist in a space other than the space in which it was created.

persistent object-(OOP) An object that exhibits persistence.

polymorphism-(OOP) The ability for methods in a class hierarchy toexhibit different behavior for the same message depending on the type ofthe object for which the method is invoked and without regard to theclass type of the reference to the object.

primary key-(DB) The key data value that uniquely identifies a recordin a database file. See also "secondary key."

primitive data type-(OOP) A data type known to the compiler. Primitivedata types in C++ are char, int, float, double, and pointer. The integertypes may be further qualified as long, short, and unsigned. All types maybe organized into arrays of like types and structures and unions of vary-ing types. Also called "intrinsic data types."

private class members-(C++) Members of a class for which access isgranted only to the class's member functions and to friend functions ofthe class.

project-(DB) A relational database operator that creates a different viewof a table from a subset of its columns. See also "select" and "join."

protected class members-(C++) Members of a class that are privateexcept to member functions of derived classes.

Page 400: C++ Database Development 1558283579

Glossary 395

prototype-(C++) The definition of a function's name, return type, andparameter list.

public class members-(C++) Members of a class to which access isgranted to all functions within the scope of an object of the class.

pure virtual function-(C++) A virtual function in a base class thatmust have a matching function in a derived class. A program may notdeclare an instance of a class that has a pure virtual function. A pro-gram may not declare an instance of a derived class if that derived classhas not provided an overriding function for each pure virtual functionin the base.

query-(DB) A process that creates a new view of the contents of a rela-tional database by applying and combining the select, project, and joinoperators. The new view is presented as another table.

record-(DB) An aggregate of data elements that form to support onefunctional aspect of a database's application. A collection of records ofcommon format forms a file.

reference-(C++) A variable name that is an alias for another variable.

relation-(DB) A relational database term that is often interchangedwith file and table, although relation does not imply any physical imple-mentation.

relational data model-(DB) A representation of data as tables of rowsand columns. Any relationships between tables are formed by commonvalues in common columns. See also "hierarchical data model" and "net-work data model."

repeating group-(DB) An array with fixed or variable dimension of sim-ilar data elements in a database record. Not permitted in a relationaldatabase.

row-(DB) Relational database term that is analogous to record in tradi-tional databases.

schema-(DB) The formal definition of the format of a database. Theschema defines the files, their format, and their relationships.

Page 401: C++ Database Development 1558283579

396 c++ Database Development

secondary key-(DB) A key data value that indexes a file on other thanits primary key. The value of a secondary key does not have to be unique.Multiple records can have the same value. When a secondary key is theprimary key of another file, the two files have a many-to-one relation-ship.

select-(DB) A relational database operator that selects rows from a tablebased on the criteria of a query. See also "project" and "join."

stream-(C++) A category of character-oriented data files or deviceswhere the data characters exist in an input or output stream.

subclass-(OOP) See "derived class."

subclassing-(OOP) See "inheritance."

superclass-(OOP) See "base class."

table-(DB) In a relational database, a file of rows and columns.

this-(C++) A pointer that exists in all nonstatic member functions. Thepointer is a pointer to an object of the class. It points to the object forwhich the function is being executed.

tuple-(DB) A relational database term that refers to a particular row ina table of rows and columns. Analogous to "record" in traditional data-bases and "object" in object databases.

type-(OOP) The type of a program constant or variable, which can beof a primitive or an abstract data type.

type conversion-(C++) The conversion of one type to another. The com-piler has built-in type conversions, and a class may define its own conver-sions for converting from an object of the class to another type and fromanother type to an object of the class.

type-safe linkage-(C++) A technique that ensures that functions andfunction calls in separately compiled program modules use consistentparameter lists.

Page 402: C++ Database Development 1558283579

Glossary 397

view-(DB) A user's perception of the data in a database, which may ormay not coincide with the actual data representation. A view is preparedfor the user by a program, typically a query program.

virtual function-(C++) A member function in a class from which otherclasses may be derived. If the derived class has a function with the samename and parameter list, the derived class's function is always executedfor objects of the derived class. See also "pure virtual function" and"overriding function."

Page 403: C++ Database Development 1558283579

Index

Aabstract base class 12, 21, 389

abstract data type 12, 14, 68, 389

abstraction 12, 14, 37, 389

adding persistent objects 71, 180

AddObject 72, 133, 139, 140,141,148,207

Annotated Reference Manual 55

anonymous object 389

ANSI protocols 162, 190

ansi.sys 162, 190

AnyKey 166, 168, 200

AppendEntry 202

argument 389

assign.cpp 175, 178

assign.h 175, 178

Assignment class 178

associative access 58, 69, 93, 142,171

BB-tree 111, 217, 218

BadKeyLength exception 212

BadMenu exception 213

'4QQ

Page 404: C++ Database Development 1558283579

400 c++ Database Development

BadObjAddr exception 212

BadReference exception 212

base class 20, 390

Basic 15

Bayer, R. 218

bool class 158, 159, 197

bool.h 158, 159

Borland C++ 6, 7, 187

BuildIndex 155

catalog 67, 390

CatKey template class 95, 104, 137,138, 143, 147, 178, 197

ChangeObject 72, 123, 133, 139,140, 141,149, 208

changing persistent objects 71,140, 180

child record 2, 31, 38, 390

class 3, 12, 14, 390

class hierarchy 390

class relationships 97

classes:

Assignment 178

bool 158, 159, 197

CatKey template 95, 104, 137,138, 143, 147, 178, 197

DataFile 104

Date 158, 161, 196, 199

Department 177

Employee 177

FamilyMember 183

GUI 158, 161, 196, 200

IndexFile 104

Key template 70, 94, 104,110, 111,132, 137, 143,147, 151,201

Key<string> specializedtemplate 115

LinkedList template 158,160, 202

Manager 178

Money 158, 161, 204

Node 104, 214

NodeFile 104

ObjAddr 125, 147, 184, 205

OneLineMenu 166, 168, 205

Parody 105, 155, 206

PdyKey 104, 127, 132, 143

Persistent 42, 70, 104, 106, 207

Page 405: C++ Database Development 1558283579

Index 401

PersistentObject template104, 125, 157, 169, 209

Project 177

Reference template 123, 183,210

ScreenMenu 166, 211

string 15, 159

TNode 104

ClearList 204

ClearScreen 164, 200

COBOL 32

CODASYL 32, 36

Codd, E.F. 65

collaboration 23, 24

column 66, 390

composition 23, 24

CompuServe 7

concatenated key 95, 117, 138

concrete data type 390

Conference on Data SystemsLanguages 32

config.sys 162, 190

connector class 97

connector file 95, 117

Conroy, John 171

constants 195

constructor 17, 390

constructor execution 44

constructor, persistent object 106,136, 137, 147

conversion database, 150

copies of persistent objects 50, 59,119, 181

creating persistent objects 139

CurrentObject 208

Ddata aggregates 85, 87

data definition language 29, 69, 390

data element 28, 390

data elements 85

data entry 163

data manipulation language 29, 390

data member 390

data member dictionary 86

data model 29

data models:

hierarchical 31

network 32

object 36, 64

Page 406: C++ Database Development 1558283579

402 C++ Database Development

proprietary 30

relational 33, 64

data nodes 214, 217

data type 28, 36

data type method 17, 18

database 28, 390

database basis 79

database conversion 150

database management system28, 391

database models 30

database name 106, 135

database requirements 81

DataFile class 104

Date class 158, 161, 196, 199

date.cpp 158, 161

date.h 158, 161, 196

Day 199

DDL 29, 69

declaration 391

definition 391

DeleteObject 72, 123, 133, 139,141,149, 181,208

deleting persistent objects 72,141,181

Department class 177

dept.cpp 175, 177

dept.h 175, 177

derived class 13, 20, 391

design notation 38

Destroy 120, 123, 182, 209

destructor 17, 391

destructor execution 45

destructor, persistent object108, 147

dialogs 166

disk data structures 213

disk files 213

DLL 6

DML 29

domain 391

DOS 7

Dr. Dobb's Journal 8

DtE1 196

dynamic link library 6

Eembedded object data members 48

Employee class 177

employee.cpp 175, 177

Page 407: C++ Database Development 1558283579

Index 403

employee.h 175, 177

encapsulation 12, 13, 15, 68, 391

enforcing relationships 118

Error 166, 200

exceptions 212

Execute 166, 167, 205, 212

extraction operator 391

Ffamily tree application 183

family.cpp 183

family.h 183

FamilyMember class 183

famtree.cpp 183, 184

famtree.exe 189, 191

file 28, 391

file header block 214, 217

file locking 7

files:

ansi.sys 162, 190

assign.cpp 175, 178

assign.h 175, 178

bool.h 158, 159

config.sys 162, 190

date.cpp 158, 161

date.h 158, 161, 196

dept.cpp 175, 177

dept.h 175, 177

employee.cpp 175, 177

employee.h 175, 177

family.cpp 183

family.h 183

famtree.cpp 183, 184

famtree.exe 189, 191

gui.cpp 161, 163, 166

gui.h 161, 163, 165, 166

index.cpp 175, 183

index.exe 189, 191

life.cpp 171, 172

life.dat 190

life.exe 189, 190, 191

life.ndx 190

linklist.h 158, 160

makefile 187, 188, 191

manager.cpp 175, 178

manager.h 175, 178

money.cpp 158, 161

money.h 158, 161

node.h 196, 214

Page 408: C++ Database Development 1558283579

404 c++ Database Development

parody.bLd 187, 188, 193

parody.lib 189

payroll.cpp 169

payroll.exe 189, 191

payroll.h 169

personel.cpp 175, 179, 180

personel.dat 190

personel.exe 189, 190

personel.h 176, 179

personel.ndx 190, 191

project.cpp 176, 177

project.h 176, 177

projview.cpp 176, 181

projview.exe 189, 191

readme.doc 188

FindEntry 203

FindObject 73, 142, 143, 145,149, 181,208

FirstEntry 203

FirstObject 73, 143, 149, 180, 208

flat file 34

free store 391

friend 391

full-screen menu 166

functional method 17

functional requirements 82

functions:

AddObject 72, 133, 139, 140,141,148,207

AnyKey 166, 168, 200

AppendEntry 202

BuildIndex 155

ChangeObject 72, 123, 133,139, 140, 141,149, 208

ClearList 204

ClearScreen 164, 200

CurrentObject 208

Day 199

DeleteObject 72, 123, 133,139, 141,149, 181,208

Destroy 120, 123, 182, 209

Error 166, 200

Execute 166, 167, 205, 212

FindEntry 203

FindObject 73, 142, 143, 145,149, 181,208

FirstEntry 203

FirstObject 73, 143, 149, 180,208

GetClassID 153, 155

GetKBChar 165, 200

Page 409: C++ Database Development 1558283579

Index 405

GetObjectHeader 155

InsertEntry 202

isNullValue 114, 143

KBCharWaiting 165, 200

Keyl 138, 197

Key2 138, 198

KeyValue 111, 137, 138, 202

KeyValuel 139, 198

KeyValue2 139, 198

LastEntry 203

LastObject 73, 143, 149, 208

LoadObject 107, 110, 128,132, 147, 207

MakeKey 114

Month 199

NextEntry 203

NextObject 74, 144, 146,149, 180, 181,208

ObjectAddress 170, 209

ObjectExists 139, 144, 146,148, 180, 181,208

OpenDataBase 206

PrevEntry 203

PreviousObject 74, 144,149, 208

PutBack 165, 200

Read 45, 106, 109, 110, 111,123, 129, 132, 139, 147,148, 151,154, 173,207

ReadKey 114

ReadObject 109, 110, 111, 123,129, 132, 150, 154,211

ReBuildIndexes 153

Rebuildlndexes 154, 155

Relate 118

RemoveEntry 203

RemoveReference 123, 211

SaveObject 108, 110, 111,130, 132, 133, 147, 207

SetCursor 164, 200

SetDay 199

SetKeyValue 109, 137, 138, 202

SetKeyValuel 139, 198

SetKeyValue2 139, 198

SetMonth 199

SetYear 199

StatusLine 164, 201

Userlnput 163, 201

UserMenu 167

Value 204

Write 45, 106, 110, 111, 123,131,132, 147, 151,173,207

Page 410: C++ Database Development 1558283579

406 C++ Database Development

WriteChar 164, 201

WriteKey 114

WriteObject 110, 111, 123,131,132, 150, 211

Year 199

YesNo 166, 181, 201

GGame of Life 157, 171

GetClassID 153, 155

GetKBChar 165, 200

GetObjectHeader 155

GNU 7

GUI class 158, 161, 196, 200

gui object 163

gui.cpp 161, 163, 166

gui.h 161, 163, 165, 166

HHAS-A relationship 24, 38

hierarchical data model 31, 391

hierarchy 2, 392

Iimplementation 13, 15, 392

implicit conversion method 17, 18

index 392

index header block 217, 218

index header node 217

index.cpp 175, 183

index.exe 189, 191

indexes 217

indexes, rebuilding 153, 191

IndexFile class 104

inheritance 12, 13, 20, 392

inheritance, multiple 2, 13, 22, 393

inheritance, single 2, 21

inheriting persistent classes 178

inline function 392

InsertEntry 202

insertion operator 392

installation 188

instantiate 13, 15, 392

integrity errors 153

integrity, persistent object 59, 72,97, 133, 148

interface 13, 15, 17, 392

Page 411: C++ Database Development 1558283579

Index 407

INTERNET 8

intrinsic data type 392

intuitive interface 16

iostream 6, 163

IS-A relationship 23, 38

isNullValue 114, 143

JJOIN operator 67, 68, 392

KKBCharWaiting 165, 200

key 392

key data elements 34

Key template class 70, 94, 104,110, 111, 132, 137, 143,147, 151,201

Keyl 138, 197

Key2 138, 198

Key<string> specialized template115

keyboard 163

keyboard scan codes 165

keyless persistent object 147,148, 149

keys 137, 142

KeyValue 111, 137, 138, 202

KeyValuel 139, 198

KeyValue2 139, 198

LLastEntry 203

LastObject 73, 143, 149, 208

Life, Game of 157, 171

life.cpp 171, 172

life.dat 190

life.exe 189, 190, 191

life.ndx 190

linkage specification 392

linked lists 160

LinkedList template class 158,160, 202

linklist.h 158, 160

LoadObject 107, 110, 128, 132,147, 207

locking, records and files 7

Page 412: C++ Database Development 1558283579

408 C++ Database Development

Mmake 189

makefile 187, 188, 191

MakeKey 114

Manager class 178

manager.cpp 175, 178

manager.h 175, 178

manipulator 392

many-to-many relationship 35,95, 97

MAXCOMMANDS 196

McCreight, E. 218

member 393

member function 20, 393

memory model 189

menus 163, 166

message 13, 17, 393

method 13, 17, 98, 393

Microsoft C++ 7

MODEL macro 189

Money class 158, 161, 204

money.cpp 158, 161

money.h 158, 161

Month 199

MS-DOS 6, 106

multiple inheritance 2, 13, 22, 393

MustDestroy exception 212

Nnavigating the database 73,

142, 180

navigational access 58, 142

network 2

network data model 32, 393

NextEntry 203

NextObject 74, 144, 146, 149,180, 181,208

NoDatabase exception 212

Node class 104, 214

node header block 220

node threads 216

node.h 196, 214

NodeFile class 104

nodelength 196

NodeNbr 196, 214, 220

nodes 214

nodes, allocating 215

nodes, deleting 215

Page 413: C++ Database Development 1558283579

Index 409

NoGUI exception 213

non-persistent members 47

normalization 35

NotInConstructor exception 212

NotInDestructor exception 212

NotLoaded exception 212

NotSaved exception 212

NULL 167, 169

0ObjAddr class 125, 147, 184, 205

objconstructed pointer 127

object 13, 393

object data 216

object data model 36, 64

object database 3, 63, 393

Object Database ManagementGroup 36, 55, 63

object header 216

object identity 57, 93

object-oriented programming 11

ObjectAddress 170, 209

ObjectExists 139, 144, 146, 148,180, 181,208

ODMG-93 36, 55

one-line menus 168

one-to-many relationship 34, 97

one-to-one relationship 97

OneLineMenu class 166, 168, 205

OpenDataBase 206

OS/2 6, 7

overloaded assignment operatorkeys, 114

overloaded function 393

overloaded operator 17, 393

overloaded relational operatorkeys, 113

overriding function 393

Pparameter 394

parameter list 394

parent file 31

parent record 2, 38, 394

Parody class 105, 155, 206

Parody object 106, 127, 135

parody.bld 187, 188, 193

parody.lib 189

Page 414: C++ Database Development 1558283579

410 C++ Database Development

payroll.cpp 169

payroll.exe 189, 191

payroll.h 169

PdyExceptions exception 212

PdyKey class 104, 127, 132, 143

performance requirements 83

persistence 394

Persistent class 42, 70, 104, 106, 207

persistent object 64, 394

address 148

classes 87

constructor 106, 136, 137, 147

copies of 50, 59, 119, 181

destructor 108, 147

integrity 59, 72, 97, 133, 148

scope 140, 141, 142

adding 71, 180

changing 71, 140, 180

creating 139

deleting 72, 141, 181

retrieving 70, 139

persistent reference 59, 122

Persistent" exception 120, 128,145, 182, 213

PersistentObject template class104, 125, 157, 169, 209

personel.cpp 175, 179, 180

personel.dat 190

personel.exe 189, 190

personel.h 176, 179

personel.ndx 190, 191

personnel application 179

Pickwood, Bobby 50

pointer data members 47

polymorphism 12, 13, 24, 68, 394

PrevEntry 203

PreviousObject 74, 144, 149, 208

primary key 34, 38, 58, 70, 73, 74,93, 94, 106, 137, 142, 394

primitive data type 14, 394

private class members 394

procedural programming 11

Project class 177

PROJECT operator 67, 68, 394

project.cpp 176, 177

project.h 176, 177

projview.cpp 176, 181

projview.exe 189, 191

proprietary data model 30

protected class members 394

prototype 395

public class members 395

Page 415: C++ Database Development 1558283579

index 411

public member function 17

pure virtual function 395

PutBack 165, 200

Qquery 395

query language 29, 30

RRead 45, 106, 109, 110, 111,

123, 129, 132, 139, 147,148, 151,154, 173,207

ReadKey 114

readme.doc 188

ReadObject 109, 110, 111, 123,129, 132, 150, 154, 211

ReBuildIndexes 153

RebuildIndexes 154, 155

rebuilding indexes 153, 191

record 28, 395

record locking 7

reference 395

reference counting 61

reference data members 48

Reference template class 123,183,210

reference, persistent 59

Relate 118

relating class objects 118

relation 66, 395

relational data model 33, 64, 395

relational database 64

relationships, enforcing 118

RemoveEntry 203

RemoveReference 123, 211

repeating group 395

report writer language 29, 30

requirements, database 81

retrieval path 97

retrieving persistent objects 70, 139

row 66, 395

RTTI 55

runtime type information 55

SSaveObject 108, 110, 111, 130,

132, 133, 147, 207

Page 416: C++ Database Development 1558283579

412 C++ Database Development

schema 29, 69, 395

scope of persistent objects 140,141,142

screen 164

SCREENHEIGHT 196

ScreenMenu class 166, 211

SCREENWIDTh 196

secondary key 34, 58, 70, 73, 74,96, 116, 137, 142, 396

SELECT operator 67, 68, 396

SetCursor 164, 200

SetDay 199

SetKeyValue 109, 137, 138, 202

SetKeyValuel 139, 198

SetKeyValue2 139, 198

SetMonth 199

SetYear 199

single inheritance 2, 21

sizeof 42, 45

Smalltalk 36

specialization 23

specialized keys 112

StatusLine 164, 201

stream 396

string class 15, 159

string keys 115

Stroupstrup, Bjarne 55

structured programming 65

structures 42

subclass 13, 20, 396

subclassing 396

superclass 13, 20, 396

Ttable 396

template classes 24

this 396

TNode class 104

tuple 66, 396

type 396

type conversion 396

type-safe linkage 396

typedef 88, 196

UUNIX 7, 9

user interface 161

UserInput 163, 201

Page 417: C++ Database Development 1558283579

Index 413

UserMenu 167

USES-A relationship 24, 38

VValue 204

varable argument list 167

view 67, 396

virtual function 49, 396

virtual function table 43, 49

virtual member function 25

vptr 43, 47, 49

vtbl 47, 49

WWindows 6, 7

Write 45, 106, 110, 111, 123, 131,132, 147, 151,173,207

WriteChar 164, 201

WriteKey 114

WriteObject 110, 111, 123, 131,132, 150, 211

XX3J16 36, 55

yYear 199

YesNo 166, 181, 201

zZerolengthKey exception 212

Page 418: C++ Database Development 1558283579

) Covers PARODY--thePersistent, Almost-RelationalObject Database Manager

Introduces object-orientedprogramming

) Teaches relational andobject-oriented ,databasemmanagment

rre

Ilete C++the text

US $34.95IsBr• 1-55828-357-9 CAN $45.95