dos_compilers/Borland Turbo Pascal v55/OOPDEMOS.DOC
2024-07-02 06:49:04 -07:00

678 lines
24 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Turbo Pascal 5.5 Documentation
Object-Oriented Programming Examples
This file documents the Turbo Pascal 5.5 Object-Oriented
Programming (OOP) examples. There are over 12,000 lines of
examples contained in the OOPDEMOS.ARC file on the disk labeled
OOP/DEMOS/BGI/DOC. (If you have a hard disk and run the INSTALL
program to install Turbo Pascal on your system, it will place the
OOP examples in C:\TP by default.)
TABLE OF CONTENTS
-----------------
1. OBJECTS.PAS - Basic object types unit
2. ODEMO.PAS - An example that uses streams and lists
3. FORMS.PAS - Implements a form entry and edit object
4. SLIDERS.PAS - Implements a slider field
5. FDEMO.PAS - A simple forms editor example
6. CARDS.PAS - Implements a card file object
7. CARDFILE.PAS - A simple card filer applciation
8. CARDGEN.PAS - Card filer forms generator
9. TCALC.PAS - See TCALC.DOC
10. Examples from the Object-Oriented Programming Guide
Four examples are included from the OOP Guide for your
convenience:
POINTS PAS - From P-20 of the OOP Guide
FIGURES PAS - From P-42 of the OOP Guide
FIGDEMO PAS - From P-47 of the OOP Guide
LISTDEMO PAS - From P-57 of the OOP Guide
These examples are fully documented in Chapter 1 of the OOP
Guide.
OBJECTS.PAS - BASIC OBJECT TYPES UNIT
-------------------------------------
The Objects unit implements two basic object types: a Stream and
a List. The Stream type is the object-oriented counterpart of a
Pascal file. Turbo Pascal 5.5 does not allow "file of object"
types, but streams may be used to implement the same
functionality, and much more as the example programs show. The
List type implements a singly-linked list of objects, each of
which must be derived from the Node type.
The Base type
-------------
Base is an abstract object type, and serves only as an ultimate
ancestor for other object types. Objects of type Base are never
instantiated. Object types derived from Base are guaranteed to
have a destructor called Done. In addition, the VMT field of
descendants of Base will always be the first field in the
descendant, which is a prerequisite of types registered with a
stream.
Unless overridden, the Done destructor in Base does nothing
except to dispose the instance when called via the extended
syntax of the Dispose standard procedure.
The Stream type
---------------
Much like an untyped file, a stream implements a number of basic
I/O capabilities, such as opening, closing, reading, writing, and
seeking. What sets a stream apart from an untyped file is its
ability to polymorphically read and write objects. This is best
demonstrated through an example.
Assume that you have three object types, Circle, Rectangle, and
Triangle, each of which are derived from an ancestor type Shape.
In order to read and write such objects to disk you would need a
FILE OF Shape that allows reading and writing of objects of type
Shape as well as descendants of Shape. For a number of reasons,
ordinary Pascal FILE types cannot achieve this. First, objects of
type Circle, Rectangle, and Triangle are most likely not of the
same size, since each will add a varying number of fields to the
basic Shape type. Thus, it would be impossible to determine the
proper record size for a FILE OF Shape. Second, a FILE OF Shape
would need to store additional type information for each object
in the file so that when reading the file, the application can
"know" the types of the objects it is reading.
The Stream type provides the solution to this problem: By
defining a Store and Load method in an object type, and by
registering that type with a stream, the stream can perform
polymorphic I/O through its Put and Get methods. The FORMS.PAS
unit and the CARDFILE.PAS program provide good examples on how to
use streams.
The Stream type is the ancestor of all other streams. It defines
the basic properties of a stream, but most of its methods are
purely abstract and meant to be overridden in descendant types.
FIELDS
TypeCount Number of types registered with the stream.
TypeList Pointer to array of VMT offsets of registered types.
ProcList Pointer to array of Store and Load method pointers.
Status Stream status. When Status is non-zero, an error
has occurred on the stream, and all subsequent
I/O is suspended. The Status field is the
equivalent of the IOResult standard function,
except that you have to manually clear Status to
re-enable I/O operations.
CONSTRUCTORS AND DESTRUCTORS
constructor Init;
Initializes the stream by allocating TypeList and ProcList, and
calling RegisterTypes to register the types known by the
stream.
destructor Done; virtual;
Disposes TypeList and ProcList.
BASIC I/O METHODS
procedure Read(var Buf; Count: Word); virtual;
Reads Count bytes from the stream into Buf. In case of error,
Buf is filled with zeros. This method must be overridden.
procedure Write(var Buf; Count: Word); virtual;
Writes Count bytes from Buf onto the stream. This method must
be overridden.
procedure Flush; virtual;
Flushes the stream's I/O buffer, if any. This method does
nothing unless overridden.
procedure Truncate; virtual;
Truncates the stream at the current position, i.e. makes the
current position the end of the stream. This method must be
overridden.
function GetPos: Longint; virtual;
Returns the current position of the stream, or -1 in case of
error.
procedure SetPos(Pos: Longint; Mode: Byte); virtual;
Sets the current position of the stream to a new position that
is Pos bytes from the stream location given by Mode. Valid Mode
values are given by the constants PosAbs, PosCur, and PosEnd,
which represent the stream's beginning, current position, and
end. This method must be overridden.
function GetSize: Longint;
Returns the current size of a stream. Calls SetPos and GetPos
to find the resulting value. This method should not be
overridden.
procedure Seek(Pos: Longint);
Seeks to the specified position. Corresponds to a call to
SetPos with a Mode value of PosAbs. This method should not be
overridden.
TYPE REGISTRATION METHODS
procedure RegisterTypes; virtual;
Registers all types that should be known to the stream. To
register types with a stream you must override this method and
call the Register method for each type. Within an overridden
RegisterTypes, always first call the inherited RegisterTypes to
register any types required by the ancestor. A type need only
be registered if instances of the type are read and written
using the Get and Put methods. Unless overridden, this method
does nothing.
procedure Register(TypePtr, StorePtr, LoadPtr: Pointer);
Registers a type with the stream. This method must only be used
within a RegisterTypes method. The format of a method call is:
Register(TypeOf(AType), @AType.Store, @AType.Load);
where AType is an object type derived from the Base object type
(i.e. an object type whose ultimate ancestor is Base). AType
must define Store and a methods with the following headers:
procedure Store(var S: Stream);
constructor Load(var S: Stream);
The Store method must write a binary representation of the
object onto the stream S (using S.Write), and the Load
constructor must read such a binary representation back from
the stream S (using S.Read).
POLYMORPHIC I/O METHODS
procedure Put(P: BasePtr);
Writes the specified object to the stream. The type of the
object must have been registered with the stream (using an
overridden RegisterTypes method). Put writes a 16-bit object
type identifier number onto the stream and then calls the
object's Store method, which writes a binary copy of the
object. The 16-bit object type identifier corresponds to the
index of the object type in the TypeList and ProcList arrays.
If the specified object pointer is NIL, a 16-bit zero is
written to the stream. If the object type has not been
registered with the stream, the stream's Error method is called
with an error code of -1.
function Get: BasePtr;
Reads an object from a stream and returns a pointer to it. Get
is the counterpart of Put, and can only read objects that were
written by Put. Get reads the 16-bit object type identifier
from the stream and then calls the object type's Load
constructor, which allocates an object on the heap and reads a
binary copy of it from the stream.
If the 16-bit object type identifier is zero, Get returns a
NIL. If the object type identifier is out of range, the
stream's error method is called with an error code of -2, and
Get returns a NIL.
ERROR HANDLING METHODS
procedure Error(Code: Integer); virtual;
This method is called whenever an error occurs on the stream.
Code contains the 16-bit error code, which for DOS file streams
is a DOS error code (see the Run-time Errors in Appendix D in
the Reference Guide). Unless overridden, the Error method just
stores Code in the stream's Status field.
The DosStream type
------------------
The DosStream type implements a DOS file stream. Data written to
a DosStream is written to a DOS file, and data read from a
DosStream is read from a DOS file.
FIELDS
Handle DOS file handle.
CONSTRUCTORS AND DESTRUCTORS
constructor Init(FileName: FNameStr; Mode: Word);
Calls Stream.Init and then opens the file given by FileName
using the access mode given by Mode. Valid Mode values are
defined by the constants SCreate, SOpenRead, SOpenWrite, and
SOpen. SCreate causes a new file to be created. SOpenRead,
SOpenWrite, and SOpen opens an existing file in read-only mode,
write-only mode, or read/write mode. The DOS file handle is
stored in the Handle field.
destructor Done; virtual;
Closes the file and then calls Stream.Done.
BASIC I/O METHODS
procedure Read(var Buf; Count: Word); virtual;
Implements Read for a DOS file. Count bytes are read from the
file into Buf using DOS function 3FH. In case of error, Buf is
filled with zeros.
procedure Write(var Buf; Count: Word); virtual;
Implements Write for a DOS file. Count bytes are written to the
file from Buf using DOS function 40H.
procedure Truncate; virtual;
Implements Truncate for a DOS file using DOS function 40H with
a count of zero.
function GetPos: Longint; virtual;
Implements GetPos for a DOS file using DOS function 42H.
procedure SetPos(Pos: Longint; Mode: Byte); virtual;
Implements SetPos for a DOS file using DOS function 42H.
procedure Open(var Name; Mode: Word);
Private method which creates or opens the file using DOS
function 3CH or 3DH. Called from DosStream.Init and should
never be called directly.
procedure Close;
Private method which flushes the stream buffer (using the Flush
method) and closes the file. Called from DosStream.Done and
should never be called directly.
The BufStream type
------------------
The BufStream type implements a buffered DOS stream. Input and
output with a BufStream is buffered in blocks of a user specified
size. When an application makes a large number of Read and Write
method calls with a small transfer size, using a BufStream rather
than a DosStream will substantially improve performance. For a
typical stream, a buffer size of 1024 bytes is suggested.
FIELDS
Buffer Pointer to an I/O buffer of BufSize bytes.
BufSize Size of I/O buffer.
BufPtr Index of current buffer position in the buffer.
BufEnd Index of current buffer end in the buffer. BufPtr
is equal to BufEnd, the buffer is empty. When
BufPtr is less than BufEnd, the buffer is in read
mode and the bytes between BufPtr and BufEnd have
been read ahead from the file. When BufPtr is
greater than BufEnd, the file is in write mode
and the bytes between BufEnd and BufPtr have been
written to the file but not yet flushed to disk.
CONSTRUCTORS AND DESTRUCTORS
constructor Init(FileName: FNameStr; Mode, Size: Word);
Allocates a buffer of Size bytes and then calls DosStream.Init.
destructor Done; virtual;
Calls DosStream.Done and then disposes the stream buffer.
BASIC I/O METHODS
procedure Read(var Buf; Count: Word); virtual;
Implements Read for a buffered stream. The stream buffer is
used to buffer disk read operations in blocks of BufSize bytes.
procedure Write(var Buf; Count: Word); virtual;
Implements Write for a buffered stream. The stream buffer is
used to buffer disk write operations in blocks of BufSize
bytes.
procedure Flush; virtual;
Flushes the stream's buffer.
function GetPos: Longint; virtual;
The Node type
-------------
Node is an abstract type that serves as the ultimate ancestor of
all objects that are kept on linked lists using the List type.
The Next field points to the next node on the list. If the node
is the last node on the list, the Next field points to the first
node, thus making the list circular. The Prev method returns a
pointer to the preceding node. If the node is the first node of
the list, the Prev method returns a pointer to the last node.
The List type
-------------
The List type implements the basic algorithms of a circular
linked list. The objects kept on a List must be derived from the
abstract Node type. Other abstract types, such as stacks and
queues, can be built from the List type, since they are simply
restricted versions of the functionality provided by List.
The List type does not inherit and has no virtual methods. For
that reason, no constructors or destructors are needed in the
List object.
FIELDS
Last Pointer to the last node on the list, or NIL if
the list is empty. The Next field of the last
node on a list, i.e. Last^.Next, points to the
first node on the list.
LIST MANAGEMENT METHODS
procedure Clear;
Clears the list by setting the Last field to NIL. Any nodes on
the list are not disposed.
procedure Delete;
Disposes all nodes on the list using their Done destructor.
procedure Append(N: NodePtr);
Appends a node. The new node becomes the first node on the
list.
procedure Insert(N: NodePtr);
Inserts a node. The new node becomes the last node on the list.
procedure Remove(N: NodePtr);
Removes a node. The node itself is not disposed.
function First: NodePtr;
Returns a pointer to the first node on the list, or NIL if the
list is empty. The last node on the list can be directly
accessed through the Last field.
function Next(N: NodePtr): NodePtr;
Returns a pointer to the node after N, or NIL if N is the last
node. This corresponds to N^.Next, except that the Next field
of the last node points to the first node in the list.
function Prev(N: NodePtr): NodePtr;
Returns a pointer to the node before N, or NIL if N is the
first node. This corresponmds to N^.Prev, except that the Prev
method of the first node will return the last node in the list.
function Empty: Boolean;
Returns True if the list is empty, else returns False.
STREAM I/O ROUTINES
procedure Store(var S: Stream);
Stores the list on a stream. The types of all nodes of the list
must have been registered with the stream. The list is stored
by applying S.Put to each node in the list, followed by an
S.Put(NIL).
procedure Load(var S: Stream);
Loads the list from a stream. The types of all nodes of the
list to be loaded must have been registered with the stream.
The list is loaded by appending the result of S.Get until S.Get
returns NIL.
ODEMO.PAS - AN EXAMPLE THAT USES STREAMS AND LISTS
--------------------------------------------------
The ODemo program demonstrates input and output of polymorphic
objects using a stream. A list of IntNode and StrNode objects is
created, written to a file, deleted, and finally reloaded from
the file. Notice how the list is built using the List type from
the Objects unit.
FORMS.PAS - IMPLEMENTS A FORM ENTRY AND EDIT OBJECT
---------------------------------------------------
The Forms unit implements two basic object types: Field and Form.
A Field represents a data entry field, and a Form represents a
collection of Fields which add up to a complete data entry form.
A number of field types are implemented by the Forms unit. Viewed
as a whole, they form the following hierarchy:
Field (Abstract field type)
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>FText (Abstract text field)
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>FStr (String field)
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>FNum (Abstract numeric field)
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>FInt (Integer field)
<20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>FZip (Zipcode field)
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>FReal (Floating point field)
The basic Field type defines the common properties of all fields.
It is an abstract type, which exists only so that other field
types can inherit from it. Instances of type Field are never
actually created. Field is derived from Node (defined in the
Objects unit), so that fields can be put on a linked list that
makes up a data entry form. The data fields of type Field are:
X X coordinate in the form.
Y Y coordinate in the form.
Size Size of the data stored in the field.
Title Pointer to a title string.
Value Pointer to current value (Size bytes long).
Extra Marks location of fields defined in descendant types.
The methods of a Field are:
Init Allocates and initializes a field.
Load Loads a field from a stream.
Done Cleans up and disposes a field.
Clear Sets a field to its default value.
Edit Edits a field and returns termination character.
Show Displays a field.
Store Stores a field on a stream.
The FText type is an abstract type which serves as the ancestor
of all text field types. It defines two "helper" methods, GetStr
and PutStr, and implements code for the Edit and Show methods.
GetStr converts a field's value to a string. PutStr converts a
string and stores it as the field's value. PutStr returns True if
the string represents a valid value; else it returns false and
leaves the field's value unchanged.
The FStr type implements a String field with a user defined
maximum length.
The FNum type is the abstract ancestor type of all numeric
fields. It changes the Show method to right justify the value
when it is displayed.
The FInt type implements a Longint field with user defined upper
and lower bounds.
The FZip type implements a zipcode field, which essentially is a
5-digit FInt field that gets left-padded with zeros when
displayed.
The FReal type implements a floating point value field with a
user defined total width and number of decimals.
The Form type defines a form, which primarily is a collection of
fields kept on a list. The data fields of type Form are:
X1 Left coordinate of Form window.
Y1 Top coordinate of Form window.
X2 Right coordinate of Form window.
Y2 Bottom coordinate of Form window.
Size Total size of data in the form. This is the sum of
the Size fields of each field on the Fields list.
Fields List of Field objects.
The methods of a Form are:
Init Allocates and initializes a form.
Load Loads a form from a stream.
Done Cleans up and disposes a form.
Edit Edits a form and returns termination character.
Show Displays a form, optionally first erasing the window.
Add Adds a field to a form.
Clear Sets all fields to their default values.
Get Copies a form into a variable.
Put Copies a variable into a form.
Store Stores a form on a stream.
The fields in a form a kept on a linked list, which is managed by
the List type in the Objects unit. Notice how the Edit, Show,
Clear, Get, and Put methods use typecasts to promote the nodes of
the list from type Node to type Field (a descendant of Node). The
Form object "knows" that the entries on the field list are always
of type Field, and can therefore safely promote them.
The Get and Put methods are used to copy data into and out of a
form. A variable specified as a parameter to Get or Put must be
record with fields that correspond in order, type, and size to
the fields of the form.
The FStream type implements a buffered stream that knows the
FStr, FInt, FZip, and FReal types. Notice how only the
RegisterTypes method is overridden, and how it first calls the
inherited RegisterTypes before registering any new types.
SLIDERS.PAS - IMPLEMENTS A SLIDER FIELD
---------------------------------------
The Sliders unit implements a new Field type, called FSlider, for
use with the Forms unit. Contrary to the text field types
implemented by the Forms unit, a slider shows and edits itself as
a scroll-bar like control with a knob that indicates the current
value. The FSlider type is a direct descendant of the Field type.
The current slider value, and the minimum, maximum, and delta
values of the slider are stored as 16-bit integers.
In addition to the FSlider type, the Sliders unit defines a new
FStream type. It is a direct descendant of the FStream type in
Forms (notice how the same name can be used in two different
units). Sliders.FStream differs from Forms.FStream only in the
RegisterTypes method, which adds registration of the FSlider
type; this is required so that sliders can be stored on and
loaded from streams along with other field types.
FDEMO.PAS - A SIMPLE FORMS EDITOR EXAMPLE
-----------------------------------------
The FDemo program uses the Forms and Sliders unit to implement a
simple forms editor program, which lets you edit a record using a
form. Notice how the fields in a Person record correspond in
order, type, and size to the fields in the form. The Form.Put and
Form.Get methods require this one-to-one mapping in order to work
correctly.
CARDS.PAS - IMPLEMENTS A CARD FILE OBJECT
-----------------------------------------
The Cards unit implements a Rolodex-like card file object. The
cards are kept on a doubly-linked list which can be traversed
using the Next and Prev methods.
The Insert and Delete methods allow insertion and deletion at the
current location in the card list. The current location is a
state variable maintained by the CardList object.
CARDFILE.PAS - A SIMPLE CARD FILER APPLCIATION
----------------------------------------------
The CardFile program implements a simple card filer application.
It takes a card file name as a command-line argument, loads that
file, and allows you to scroll through and edit it. A card file
contains a saved Form object followed by a saved CardList object.
The CardFile application is not limited to a specific form
layout--the form is loaded from the a disk file rather than being
statically built in CARDFILE.PAS.
A sample card file, CARDS.DTA, is provided on the disk. To run
the card filer with that file, use the command line:
CARDFILE CARDS.DTA
CARDGEN.PAS - CARD FILER FORMS GENERATOR
----------------------------------------
CARDGEN.PAS is a sample program that defines two forms and saves
them in disk files that can be edited using CARDFILE.PAS. If you
run CARDGEN.PAS, it will create new PARTS.DTA and PEOPLE.DTA
files (and thus overwrite any existing files with those names, so
beware).