Delphi Object Pascal Language Guide
Delphi Object Pascal Language Guide
This manual is about the Object Pascal language as it is used in Delphi. For an overview
of the Delphi documentation, see the introduction to the Delphi User’s Guide. This
manual
• Presents the formal definition of the Object Pascal language
• Describes what goes on inside an Object Pascal application in regard to memory, data
formats, calling conventions, input and output, and automatic optimizations
• Explains how to use assembly language in Object Pascal applications
• Introduces the command-line compiler for Delphi
• Explains the use of compiler directives
• Presents explanations of error messages
Syntax Diagrams
In this book you’ll encounter syntax diagrams. For example:
Introduction 1
procedure heading
procedure identifier
qualified method identifier
To read a syntax diagram, follow the arrows. Frequently, more than one path is
possible. The above diagram indicates that a formal parameter list is optional in a
procedure heading. You can follow the path from the indentifier to the end of the
procedure heading, or you can follow it to the formal parameter list before reaching
the end.
The names in the boxes stand for constructions. Those in circles — reserved words.
operators, and punctuation — are actual terms used in the programs; they are
boldfaced in the diagrams.
1
Tokens
Tokens are the smallest meaningful units of text in an Object Pascal program. They
are categorized as special symbols, identifiers, labels, numbers, and string constants.
An Object Pascal program is made up of tokens and separators. A separator is either
a blank or a comment. Two adjacent tokens must be separated by one or more
separators if each token is a reserved word, an identifier, a label, or a number.
Separators can’t be part of tokens except in string constants.
Special symbols
Object Pascal uses the following subsets of the ASCII character set:
• Letters—the English alphabet, A through Z and a through z
• Digits—the Arabic numerals 0 through 9
• Hex digits—the Arabic numerals 0 through 9, the letters A through F, and the
letters a through f
• Blanks—the space character (ASCII 32) and all ASCII control characters (ASCII 0
through 31), including the end-of-line or return character (ASCII 13)
These are the syntax diagrams for letter, digit, and hex digit:
letter
A ... Z a ... z
digit
0 ... 9
Chapter 1, Tokens 3
hex digit digit
A ... F a ... f
Special symbols and reserved words are characters that have one or more fixed
meanings. The following single characters are special symbols:
+ - */ = < > [ ] . , ( ) : ; ^ @ { } $ #
These character pairs are also special symbols:
<= >= := .. (* *) (. .)
A left bracket ([) is equivalent to the character pair of left parenthesis and a period—
(., and a right bracket (]) is equivalent to the character pair of a period and a right
parenthesis—.). Likewise, a left brace ({) is equivalent to the character pair of left
parenthesis and an asterisk—(*, and a right brace (}) is equivalent to the character
pair of an asterisk and a right parenthesis—*).
The following are Object Pascal’s standard directives. Directives are used only in
contexts where user-defined identifiers can’t occur. Unlike reserved words, you can
redefine standard directives, but we advise that you don’t.
private, protected, public, and published act as reserved words within object type
declarations, but are otherwise treated as directives.
Identifiers
Identifiers denote constants, types, variables, procedures, functions, units,
programs, and fields in records.
An identifier can be of any length, but only the first 63 characters are significant. An
identifier must begin with a letter or an underscore character (_) and can’t contain
spaces. Letters, digits, and underscore characters (ASCII $5F) are allowed after the
first character. Like reserved words, identifiers are not case sensitive.
When several instances of the same identifier exist, you may need to qualify the
identifier by another identifier to select a specific instance. For example, to qualify
the identifier Ident by the unit identifier UnitName, write UnitName.Ident. The
combined identifier is called a qualified identifier. Units are described on page 206 of
the Delphi User’s Guide and Chapter 11 of this manual.
identifier letter
underscore letter
digit
underscore
underscore _
qualified
identifier identifier
.
Chapter 1, Tokens 5
Numbers
Ordinary decimal notation is used for numbers that are constants of integer and real
types. A hexadecimal integer constant uses a dollar sign ($) as a prefix. Engineering
notation (E or e, followed by an exponent) is read as “times ten to the power of” in
real types. For example, 7E-2 means 7 x 10-2; 12.25e+6 or 12.25e6 both mean 12.25 x
10+6. Syntax diagrams for writing numbers follow:
hex digit sequence hex digit
sign +
-
unsigned real
digit sequence . digit sequence
scale factor
Labels
A label is a digit sequence in the range 0 to 9999. Leading zeros are not significant.
Labels are used with goto statements.
label digit sequence
identifier
Character strings
A character string is a sequence of zero or more characters from the extended ASCII
character set, written on one line in the program and enclosed by apostrophes.
A character string with nothing between the apostrophes is a null string. Two
sequential apostrophes in a character string denote a single character, an
apostrophe. For example,
'BORLAND' { BORLAND }
'You''ll see' { You'll see }
'''' { ' }
'' { null string }
' ' { a space }
Object Pascal lets you embed control characters in character strings. The # character
followed by an unsigned integer constant in the range 0 to 255 denotes a character of
the corresponding ASCII value. There must be no separators between the #
character and the integer constant. Likewise, if several are part of a character string,
there must be no separators between them. For example,
#13#10
'Line 1'#13'Line2'
#7#7'Wake up!'#7#7
quoted string ’ ’
string character
Comments
The following constructs are comments and are ignored by the compiler:
Chapter 1, Tokens 7
{ Any text not containing right brace }
(* Any text not containing star/right parenthesis *)
A comment that contains a dollar sign ($) immediately after the opening { or (* is a
compiler directive. A mnemonic of the compiler command follows the $ character.
Program lines
Object Pascal program lines have a maximum length of 126 characters.
Constants
2
A constant is an identifier that marks a value that can’t change. A constant declaration
declares a constant within the block containing the declaration. A constant identifier
can’t be included in its own declaration.
constant declaration identifier = constant ;
Chapter 2, Constants 9
• The address operator (@) (except in constant address expressions as described on
page 35)
Except for these restrictions, constant expressions follow the syntactical rules as
ordinary expressions. For expression syntax, see Chapter 5, “Expressions.”
The following standard functions are allowed in constant expressions:
Ab Length Ord SizeOf
Chr Lo Pred Succ
Hi Low Ptr Swap
High Odd Round Trunc
Here are some examples of the use of constant expressions in constant declarations:
const
Min = 0;
Max = 100;
Center = (Max - Min) div 2;
Beta = Chr(225);
NumChars = Ord('Z') - Ord('A') + 1;
Message = 'Out of memory';
ErrStr = ' Error: ' + Message + '. ';
ErrPos = 80 - Length(ErrStr) div 2;
Ln10 = 2.302585092994045684;
Ln10R = 1 / Ln10;
Numeric = ['0'..'9'];
Alpha = ['A'..'Z', 'a'..'z'];
AlphaNum = Alpha + Numeric;
3
Types
When you declare a variable, you must state its type. A variable’s type circumscribes
the set of values it can have and the operations that can be performed on it. A type
declaration specifies the identifier that denotes a type.
type declaration identifier = type ;
When an identifier occurs on the left side of a type declaration, it’s declared as a type
identifier for the block in which the type declaration occurs. A type identifier’s scope
doesn’t include itself except for pointer types.
type simple type
string type
structured type
pointer type
procedural type
type identifier
There are six major type classes. They are described in the following sections.
Simple types
Simple types define ordered sets of values.
simple type ordinal type
real type
Chapter 3, Types 11
A real type identifier is one of the standard identifiers: Real, Single, Double, Extended,
or Comp. Chapter 1 explains how to denote constant integer type and real type
values.
Ordinal types
Ordinal types are a subset of simple types. All simple types other than real types are
ordinal types, which are set off by six characteristics:
• All possible values of a given ordinal type are an ordered set, and each possible
value is associated with an ordinality, which is an integral value. Except for
integer type values, the first value of every ordinal type has ordinality 0, the next
has ordinality 1, and so on for each value in that ordinal type. The ordinality of
an integer type value is the value itself. In any ordinal type, each value other than
the first has a predecessor, and each value other than the last has a successor
based on the ordering of the type.
• The standard function Ord can be applied to any ordinal type value to return the
ordinality of the value.
• The standard function Pred can be applied to any ordinal type value to return the
predecessor of the value. If applied to the first value in the ordinal type and if
range checking is enabled {$R+}, Pred produces a run-time error.
• The standard function Succ can be applied to any ordinal type value to return the
successor of the value. If applied to the last value in the ordinal type and if range
checking is enabled {$R+}, Succ produces a run-time error.
• The standard function Low can be applied to an ordinal type identifier and to a
variable reference of an ordinal type. The result is the lowest value in the range of
the given ordinal type.
• The standard function High can be applied to an ordinal type identifier and to a
variable reference of an ordinal type. The result is the highest value in the range
of the given ordinal type.
The syntax of an ordinal type follows:
ordinal type subrange type
enumerated type
ordinal type identifier
Object Pascal has twelve predefined ordinal types: Integer, Shortint, Smallint, Longint,
Byte, Word, Cardinal, Boolean, ByteBool, WordBool, LongBool, and Char. In addition,
there are two other classes of user-defined ordinal types: enumerated types and
subrange types.
Integer types
Object Pascal's predefined integer types are divided into two categories: fundamental
types and generic types. The range and format of the fundamental types is
independent of the underlying CPU and operating system and does not change
The generic integer types are Integer and Cardinal. The Integer type represents a
generic signed integer, and the Cardinal type represents a generic unsigned integer.
The actual ranges and storage formats of the Integer and Cardinal vary across
different implementations of Object Pascal, but are generally the ones that result in
the most efficient integer operations for the underlying CPU and operating system.
Table 3-2 Generic integer types for 16-bit implementations of Object Pascal
Type Range Format
Integer -32768..32767 Signed 16-bit
Cardinal 0..65535 Unsigned 16-bit
Table 3-3 Generic integer types for 32-bit implementations of Object Pascal
Type Range Format
Integer -2147483648..2147483647 Signed 32-bit
Cardinal 0..2147483647 Unsigned 32-bit
Applications should use the generic integer formats whenever possible, since they
generally result in the best performance for the underlying CPU and operating
system. The fundamental integer types should be used only when the actual range
and/or storage format matters to the application.
Arithmetic operations with integer-type operands use 8-bit, 16-bit, or 32-bit
precision, according to the following rules:
• The type of an integer constant is the predefined integer type with the smallest
range that includes the value of the integer constant.
• For a binary operator (an operator that takes two operands), both operands are
converted to their common type before the operation. The common type is the
predefined integer type with the smallest range that includes all possible values
of both types. For example, the common type of Smallint and Byte is Smallint, and
the common type of Smallint and Word is Longint. The operation is performed
using the precision of the common type, and the result type is the common type.
• The expression on the right of an assignment statement is evaluated
independently from the size or type of the variable on the left.
Chapter 3, Types 13
• Any byte-sized operand is converted to an intermediate word-sized operand that
is compatible with both Smallint and Word before any arithmetic operation is
performed.
An integer-type value can be explicitly converted to another integer type through
typecasting. Typecasting is described in Chapters 4 and 5.
Boolean types
There are four predefined boolean types: Boolean, ByteBool, WordBool, and LongBool.
Boolean values are denoted by the predefined constant identifiers False and True.
Because booleans are enumerated types, these relationships hold:
• False < True
• Ord(False) = 0
• Ord(True) = 1
• Succ(False) = True
• Pred(True) = False
Boolean and ByteBool variables occupy one byte, a WordBool variable occupies two
bytes (one word), and a LongBool variable occupies four bytes (two words). Boolean
is the preferred type and uses the least memory; ByteBool, WordBool, and LongBool
primarily exist to provide compatibility with other languages and the Windows
environment.
A Boolean variable can assume the ordinal values 0 and 1 only, but variables of type
ByteBool, WordBool, and LongBool can assume other ordinal values. An expression of
type ByteBool, WordBool, or LongBool is considered False when its ordinal value is
zero, and True when its ordinal value is nonzero. Whenever a ByteBool, WordBool, or
LongBool value is used in a context where a Boolean value is expected, the compiler
will automatically generate code that converts any nonzero value to the value True.
Char type
Char’s set of values are characters, ordered according to the extended ASCII
character set. The function call Ord(Ch), where Ch is a Char value, returns Ch’s
ordinality.
A string constant of length 1 can denote a constant character value. Any character
value can be generated with the standard function Chr.
Enumerated types
Enumerated types define ordered sets of values by enumerating the identifiers that
denote these values. Their ordering follows the sequence the identifiers are
enumerated in.
enumerated type ( identifier list )
Subrange types
A subrange type is a range of values from an ordinal type called the host type. The
definition of a subrange type specifies the smallest and the largest value in the
subrange; its syntax follows:
subrange type constant .. constant
Both constants must be of the same ordinal type. Subrange types of the form A..B
require that A is less than or equal to B.
These are examples of subrange types:
0..99
-128..127
Club..Heart
A variable of a subrange type has all the properties of variables of the host type, but
its run-time value must be in the specified interval.
One syntactic ambiguity arises from allowing constant expressions where Standard
Pascal only allows simple constants. Consider the following declarations:
const
X = 50;
Y = 10;
type
Color = (Red, Green, Blue);
Scale = (X - Y) * 2..(X + Y) * 2;
Standard Pascal syntax dictates that, if a type definition starts with a parenthesis,
it’s an enumerated type, such as the Color type in the previous example. The intent
of the declaration of scale is to define a subrange type, however. The solution is to
reorganize the first subrange expression so that it doesn’t start with a parenthesis, or
to set another constant equal to the value of the expression and use that constant in
the type definition:
Chapter 3, Types 15
type
Scale = 2 * (X - Y)..(X + Y) * 2;
Real types
A real type has a set of values that is a subset of real numbers, which can be
represented in floating-point notation with a fixed number of digits. A value’s
floating-point notation normally comprises three values—M, B, and E—such that M
x BE = N, where B is always 2, and both M and E are integral values within the real
type’s range. These M and E values further prescribe the real type’s range and
precision.
There are five kinds of real types: Real, Single, Double, Extended, and Comp. The real
types differ in the range and precision of values they hold as shown in the following
table:
Table 3-4 Real data types
Type Range Significant digits Size in bytes
-39 38
Real 2.9 x 10 .. 1.7 x 10 11-12 6
-45 38
Single 1.5 x 10 .. 3.4 x 10 7-8 4
-324 308
Double 5.0 x 10 .. 1.7 x 10 15-16 8
-4932 4932
Extended 3.4 x 10 .. 1.1 x 10 19-20 10
63 63
Comp -2 +1 .. 2 -1 19-20 8
The Comp type holds only integral values within -263+1 to 263 -1, which is
approximately -9.2 x 1018 to 9.2 x 1018.
Object Pascal supports two models of code generation for performing real-type
operations: software floating point and 80x87 floating point. The $N compiler
directive is used to select the appropriate model.
The ordering between any two string values is set by the ordering relationship of the
character values in corresponding positions. In two strings of unequal length, each
character in the longer string without a corresponding character in the shorter string
takes on a higher or greater-than value; for example, ‘xs’ is greater than ‘x’. Null
strings can be equal only to other null strings, and they hold the least string values.
Characters in a string can be accessed as components of an array. See “Arrays,
strings, and indexes” on page 32.
The Low and High standard functions can be applied to a string-type identifier and
to a variable reference of a string type. In this case, Low returns zero, and High
returns the size attribute (maximum length) of the given string.
A variable parameter declared using the OpenString identifier, or using the string
keyword in the {$P+} state, is an open string parameter. Open string parameters allow
string variables of varying sizes to be passed to the same procedure or function.
Read about open string parameters on page 78.
Structured types
A structured type, characterized by its structuring method and by its component
type(s), holds more than one value. If a component type is structured, the resulting
structured type has more than one level of structuring. A structured type can have
unlimited levels of structuring, but the maximum permitted size of any structured
type in Object Pascal is 65,520 bytes.
structured type array type
class type
set type
file type
In Standard Pascal, the word packed in a structured type’s declaration tells the
compiler to compress data storage, even at the cost of diminished access to a
Chapter 3, Types 17
component of a variable of this type. In Object Pascal, however, packed has no
effect; instead packing occurs automatically whenever possible.
Class types and class reference types are the cornerstones of object oriented
programming in Object Pascal. They are described in full in Chapter 9, "Classes".
Array types
Arrays have a fixed number of components of one type—the component type. In the
following syntax diagram, the component type follows the word of.
array type
array [ index type ] of type
,
The index types, one for each dimension of the array, specify the number of
elements. Valid index types are all ordinal types except Longint and subranges of
Longint. The array can be indexed in each dimension by all values of the
corresponding index type; therefore, the number of elements is the product of the
number of values in each index type.
The following is an example of an array type:
array[1..100] of Real
If an array type’s component type is also an array, you can treat the result as an
array of arrays or as a single multidimensional array. For example,
array[Boolean] of array[1..10] of array[Size] of Real
is interpreted the same way by the compiler as
array[Boolean,1..10,Size] of Real
You can also express
packed array[1..10] of packed array[1..8] of Boolean
as
packed array[1..10,1..8] of Boolean
You access an array’s components by supplying the array’s identifier with one or
more indexes in brackets. See “Arrays, strings, and indexes” on page 32.
When applied to an array-type identifier or a variable reference of an array type, the
Low and High standard functions return the low and high bounds of the index type
of the array.
An array type of the form
packed array[M..N] of Char
where M is less than N is called a packed string type (the word packed can be omitted
because it has no effect in Object Pascal). A packed string type has certain properties
Record types
A record type comprises a set number of components, or fields, that can be of
different types. The record-type declaration specifies the type of each field and the
identifier that names the field.
record type record end
field list
field list
fixed part
; variant part ;
The fixed part of a record type sets out the list of fixed fields, giving an identifier
and a type for each. Each field contains information that is always retrieved in the
same way.
The following is an example of a record type:
type
TDateRec = record
Year: Integer;
Month: 1..12;
Day: 1..31;
end;
The variant part shown in the syntax diagram of a record-type declaration
distributes memory space for more than one list of fields, so the information can be
accessed in more ways than one. Each list of fields is a variant. The variants overlay
the same space in memory, and all fields of all variants can be accessed at all times.
Chapter 3, Types 19
variant part
case tag field type of variant
identifier : ;
variant constant : ( )
, field list
You can see from the diagram that each variant is identified by at least one constant.
All constants must be distinct and of an ordinal type compatible with the tag field
type. Variant and fixed fields are accessed the same way.
An optional identifier, the tag field identifier, can be placed in the variant part. If a tag
field identifier is present, it becomes the identifier of an additional fixed field—the
tag field—of the record. The program can use the tag field’s value to show which
variant is active at a given time. Without a tag field, the program selects a variant by
another criterion.
Some record types with variants follow:
type
TPerson = record
FirstName, LastName: string[40];
BirthDate: TDate;
case Citizen: Boolean of
True: (BirthPlace: string[40]);
False: (Country: string[20];
EntryPort: string[20];
EntryDate: TDate;
ExitDate: TDate);
end;
TPolygon = record
X, Y: Real;
case Kind: Figure of
TRectangle: (Height, Width: Real);
TTriangle: (Side1, Side2, Angle: Real);
TCircle: (Radius: Real);
end;
Set types
A set type’s range of values is the power set of a particular ordinal type (the base
type). The power set is the set of all possible subsets of values of the base type
including the empty set. Therefore, each possible value of a set type is a subset of
the possible values of the base type.
A variable of a set type can hold from none to all the values of the set. Set-type
operators are described in the section “Set operators” in Chapter 5. “Set
constructors” in the same chapter shows how to construct set values.
The base type must not have more than 256 possible values, and the ordinal values
of the upper and lower bounds of the base type must be within the range 0 to 255.
Every set type can hold the value [ ], which is called the empty set.
File types
A file type consists of a linear sequence of components of the component type,
which can be of any type except a file type, any structured type with a file-type
component, or an object type. The number of components isn’t set by the file-type
declaration.
file type file of type
If the word of and the component type are omitted, the type denotes an untyped
file. Untyped files are low-level I/O (input/output) channels primarily used for
direct access to any disk file regardless of its internal format.
The standard file type Text signifies a file containing characters organized into lines.
Text files use special I/O procedures, which are discussed in Chapter 13, “Input and
output.”
Pointer types
A pointer type defines a set of values that point to dynamic variables of a specified
type called the base type. A pointer-type variable contains the memory address of a
dynamic variable.
pointer type ^ base type
If the base type is an undeclared identifier, it must be declared in the same type
declaration part as the pointer type.
You can assign a value to a pointer variable with the New procedure, the @ operator,
the Ptr function, or the GetMem procedure. New allocates a new memory area in the
application heap for a dynamic variable and stores the address of that area in the
pointer variable. The @ operator directs the pointer variable to the memory area
containing any existing variable or procedure or function entry point, including
variables that already have identifiers. Ptr points the pointer variable to a specific
memory address. GetMem creates a new dynamic variable of a specified size, and
puts the address of the block in the pointer variable.
The reserved word denotes a pointer-valued constant that doesn’t point to anything.
Chapter 3, Types 21
Type Pointer
The predefined type Pointer denotes an untyped pointer; that is, a pointer that
doesn’t point to any specific type. Variables of type Pointer can’t be dereferenced;
writing the pointer symbol ^ after such a variable is an error. Generic pointers,
however, can be typecast to allow dereferencing. Like the value denoted by the
word nil, values of type Pointer are compatible with all other pointer types. For the
syntax of referencing the dynamic variable pointed to by a pointer variable, see
Chapter 4’s section entitled “Pointers and dynamic variables” on page 33.
Type PChar
Object Pascal has a predefined type, PChar, to represent a pointer to a null-
terminated string. The System unit declares PChar as
type PChar = ^Char;
Object Pascal supports a set of extended syntax rules to facilitate handling of null-
terminated strings using the PChar type. For a complete discussion of this topic, see
Chapter 15, “Using null-terminated strings.”
Procedural types
Object Pascal allows procedures and functions to be treated as entities that can be
assigned to variables and passed as parameters. Such actions are made possible
through procedural types.
procedural type
procedure
formal parameter list of object
function : result
formal parameter list
Method pointers
A procedural type declared with the of object clause is called a method pointer. A
method pointer can reference a procedure or function method of an object, and is
encoded as two pointers. The first pointer stores the address of a method, and the
second pointer stores a reference to the object that the method belongs to. Some
examples of method pointer types follow:
type
TMethod = procedure of object;
TNotifyEvent = procedure(Sender: TObject) of object;
Procedural values
A variable of a procedural type can be assigned a procedural value. Procedural values
can be one of the following:
• The value nil
• A variable reference of a procedural type
• A global procedure or function identifier
• A method designator
In the context of procedural values, a global procedure or function identifier denotes
a global procedure pointer value, and a method designator denotes a method
pointer value. For example, given the following declarations:
type
TMainForm = class(TForm)
procedure ButtonClick(Sender: TObject);
...
end;
var
MainForm: TMainForm;
MathFunc: TMathFunc;
OnClick: TNotifyEvent;
Chapter 3, Types 23
Using a procedural variable that contains the value nil in a procedure statement or
function call results in an error. The value nil is intended to indicate that a
procedural variable is unassigned, and whenever there is a possibility that a
procedural value is nil, procedure statements or function calls involving that
procedural variable should be guarded by a test:
if Assigned(OnClick) then OnClick(Self);
The Assigned standard function returns True if the given procedural variable has
been assigned a procedural value, or False if the procedural variable contains nil.
Type compatibility
Compatibility between two types is sometimes required, such as in expressions or in
relational operations. Type compatibility is important, however, as a precondition of
assignment compatibility.
Type compatibility exists when at least one of the following conditions is true:
• Both types are the same.
• Both types are real types.
• Both types are integer types.
• One type is a subrange of the other.
• Both types are subranges of the same host type.
• Both types are set types with compatible base types.
• Both types are packed string types with an identical number of components.
Chapter 3, Types 25
• One type is a string type and the other is either a string type, packed string type,
or Char type.
• One type is Pointer and the other is any pointer type.
• Both types are class types or class reference types, and one type is derived from
the other.
• One type is PChar and the other is a zero-based character array of the form
array[0..X] of Char. (This applies only when extended syntax is enabled with the
{$X+} directive.)
• Both types are pointers to identical types. (This applies only when type-checked
pointers are enabled with the {$T+} directive.)
• Both types are procedural types with identical result types, an identical number
of parameters, and a one-to-one identity between parameter types.
Assignment compatibility
Assignment compatibility is necessary when a value is assigned to something, such
as in an assignment statement or in passing value parameters.
A value of type T2 is assignment-compatible with a type T1 (that is, T1 := T2 is
allowed) if any of the following are true:
• T1 and T2 are identical types and neither is a file type or a structured type that
contains a file-type component at any level of structuring.
• T1 and T2 are compatible ordinal types, and the values of type T2 falls within
the range of possible values of T1 .
• T1 and T2 are real types, and the value of type T2 falls within the range of
possible values of T1.
• T1 is a real type, and T2 is an integer type.
• T1 and T2 are string types.
• T1 is a string type, and T2 is a Char type.
• T1 is a string type, and T2 is a packed string type.
• T1 and T2 are compatible, packed string types.
• T1 and T2 are compatible set types, and all the members of the value of type T2
fall within the range of possible values of T1 .
• T1 and T2 are compatible pointer types.
• T1 is a class type and T2 is a class type derived from T1.
• T1 is a class reference type and T2 is a class reference type derived from T1.
• T1 is a PChar and T2 is a string constant. (This applies only when extended
syntax is enabled {$X+}.)
Chapter 3, Types 27
TPersonBuf = array[0..SizeOf(TPersonData)-1] of Byte;
TPeople = file of TPersonData;
In the example, TRange, TNumber, and Integer are identical types. TTestIndex is
compatible and assignment-compatible with, but not identical to, the types
TNumber, TRange, and Integer. Notice the use of constant expressions in the
declarations of TCharVal and TPersonBuf.
The type given for the variable(s) can be a type identifier previously declared in a
type declaration part in the same block, in an enclosing block, or in a unit; it can also
be a new type definition.
When an identifier is specified within the identifier list of a variable declaration, that
identifier is a variable identifier for the block in which the declaration occurs. The
variable can then be referred to throughout the block, unless the identifier is
redeclared in an enclosed block. Redeclaration creates a new variable using the
same identifier, without affecting the value of the original variable.
An example of a variable declaration part follows:
var
X, Y, Z: Double;
I, J, K: Integer;
Digit: 0..9;
C: Color;
Done, Error: Boolean;
Operator: (Plus, Minus, Times);
Hue1, Hue2: set of Color;
Today: Date;
Results: MeasureList;
P1, P2: Person;
Absolute variables
Variables can be declared to reside at specific memory addresses, and are then
called absolute variables. The declaration of such variables must include an absolute
clause following the type:
absolute clause
absolute unsigned integer : unsigned integer
variable identifier
Note The variable declaration’s identifier list can specify only one identifier when an
absolute clause is present.
Variable references
A variable reference signifies one of the following:
• A variable
• A component of a structured- or string-type variable
• A dynamic variable pointed to by a pointer-type variable
This is the syntax of a variable reference:
variable reference variable identifier
qualifier
variable typecast
expression qualifier
Note The syntax for a variable reference allows an expression that computes a pointer
type value. The expression must be followed by a qualifier that dereferences the
pointer value (or indexes the pointer value if the extended syntax is enabled with
the {$X+} directive) to produce an actual variable reference.
An array identifier with no qualifier, for example, references the entire array:
Results
An array identifier followed by an index denotes a specific component of the
array—in this case, a structured variable:
Results[Current + 1]
With a component that is a record or object, the index can be followed by a field
designator. Here the variable access signifies a specific field within a specific array
component:
Results[Current + 1].Data
The field designator in a pointer field can be followed by the pointer symbol (^) to
differentiate between the pointer field and the dynamic variable it points to:
Results[Current + 1].Data^
If the variable being pointed to is an array, indexes can be added to denote
components of this array:
Results[Current + 1].Data^[J]
Variable typecasts
Variable typecasting changes the variable reference of one type into a variable
reference of another type. The programmer is responsible for determining the
validity of a typecast.
variable typecast
type identifier ( variable reference )
begin
W := $1234;
B := TByteRec(W).Lo;
TByteRec(W).Hi := 0;
Typed constants
Typed constants can be compared to initialized variables—variables whose values
are defined on entry to their block. Unlike an untyped constant, the declaration of a
typed constant specifies both the type and the value of the constant.
typed constant declaration
identifier : type = typed constant
Typed constants can be used exactly like variables of the same type and they can
appear on the left-hand side in an assignment statement. Note that typed constants
are initialized only once—at the beginning of a program. Therefore, for each entry to
a procedure or function, the locally declared typed constants aren’t reinitialized.
In addition to a normal constant expression, the value of a typed constant can be
specified using a constant-address expression. A constant-address expression is an
expression that involves taking the address, offset, or segment of a global variable, a
typed constant, a procedure, or a function. Constant-address expressions can’t
reference local variables (stack-based) or dynamic (heap-based) variables, because
their addresses can’t be computed at compile time.
Simple-type constants
Declaring a typed constant as a simple type specifies the value of the constant:
const
Maximum: Integer = 9999;
Factor: Real = -0.1;
Breakchar: Char = #3;
As mentioned earlier, the value of a typed constant can be specified using a
constant-address expression, that is, an expression that takes the address, offset, or
segment of a global variable, a typed constant, a procedure, or a function. For
example,
var
Buffer: array[0..1023] of Byte;
const
BufferOfs: Word = Ofs(Buffer);
BufferSeg: Word = Seg(Buffer);
Because a typed constant is actually a variable with a constant value, it can’t be
interchanged with ordinary constants. For example, it can’t be used in the
declaration of other constants or types:
const
Min: Integer = 0;
Max: Integer = 99;
type
Vector = array[Min..Max] of Integer;
The Vector declaration is invalid because Min and Max are typed constants.
Structured-type constants
The declaration of a structured-type constant specifies the value of each of the
structure’s components. Object Pascal supports the declaration of array, record, and
set type constants. File type constants and constants of array, and record types that
contain file type components aren’t allowed.
Array-type constants
The declaration of an array-type constant, enclosed in parentheses and separated by
commas, specifies the values of the components.
array constant ( typed constant )
,
Record-type constants
The declaration of a record-type constant specifies the identifier and value of each
field, enclosed in parentheses and separated by semicolons.
record constant
( field identifier : typed constant )
;
Set-type constants
Just like a simple-type constant, the declaration of a set-type constant specifies the
value of the set using a constant expression. Here are some examples:
type
TDigits = set of 0..9;
TLetters = set of 'A'..'Z';
const
EvenDigits: TDigits = [0, 2, 4, 6, 8];
Vowels: TLetters = ['A', 'E', 'I', 'O', 'U', 'Y'];
HexDigits: set of '0'..'z' = ['0'..'9', 'A'..'F', 'a'...f'];
Pointer-type constants
The declaration of a pointer-type constant uses a constant-address expression to
specify the pointer value. Some examples follow:
type
TDirection = (Left, Right, Up, Down);
TStringPtr = ^String;
TNodePtr = ^Node;
TNode = record
Next: TNodePtr;
Symbol: TStringPtr;
Value: TDirection;
end;
const
S1: string[4] = 'DOWN';
S2: string[2] = 'UP';
S3: string[5] = 'RIGHT';
S4: string[4] = 'LEFT';
N1: TNode = (Next: nil; Symbol: @S1; Value: Down);
N2: TNode = (Next: @N1; Symbol: @S2; Value: Up);
N3: TNode = (Next: @N2; Symbol: @S3; Value: Right);
N4: TNode = (Next: @N3; Symbol: @S4; Value: Left);
DirectionTable: TNodePtr = @N4;
When the extended syntax is enabled (using a {$X+} compiler directive), a typed
constant of type PChar can be initialized with a string constant. For example,
const
Procedural-type constants
A procedural-type constant must specify the identifier of a procedure or function
that is assignment-compatible with the type of the constant, or it must specify the
value nil.
procedural constant procedure identifier
function identifier
nil
Here’s an example:
type
TErrorProc = procedure(ErrorCode: Integer);
const
ErrorHandler: TErrorProc = DefaultError;
Expressions
5
Expressions are made up of operators and operands. Most Object Pascal operators are
binary; they take two operands. The rest are unary and take only one operand.
Binary operators use the usual algebraic form (for example, A + B). A unary
operator always precedes its operand (for example, -B).
In more complex expressions, rules of precedence clarify the order in which
operations are performed.
Table 5-1 Precedence of operators
Operators Precedence Categories
@, not first (high) unary operators
*, /, div, mod, and, shl, shr, as second multiplying operators
+,-, or, xor third adding operators
=, <>, <, >, <=, >=, in, is fourth (low) relational operators
Expression syntax
The precedence rules follow from the syntax of expressions, which are built from
factors, terms, and simple expressions.
Chapter 5, Expressions 41
A factor’s syntax follows:
factor variable reference
unsigned constant
( expression )
not factor
sign factor
function call
set constructor
value typecast
address factor
A function call activates a function and denotes the value returned by the function.
See “Function calls” on page 50.
A set constructor denotes a value of a set type. See “Set constructors” on page 50.
A value typecast changes the type of a value. See “Value typecasts” on page 51.
An address factor computes the address of a variable, procedure, function, or
method. See “The @ operator” on page 49.
An unsigned constant has the following syntax:
unsigned constant unsigned number
character string
constant identifier
nil
Chapter 5, Expressions 43
Done <> Error
(I < J) = (J < K)
C in Hue1
Operators
Operators are classified as arithmetic operators, logical operators, string operators,
character-pointer operators, set operators, relational operators, and the @ operator.
Arithmetic operators
The following tables show the types of operands and results for binary and unary
arithmetic operations.
Table 5-2 Binary arithmetic operations
Operator Operation Operand types Result type
+ addition integer type integer type
real type real type
- subtraction integer type integer type
real type real type
* multiplication integer type integer type
real type real type
/ division integer type real type
real type real type
div integer division integer type integer type
mod remainder integer type integer type
The + operator is also used as a string or set operator, and the +, -, and * operators
are also used as set operators.
Table 5-3 Unary arithmetic operations
Operator Operation Operand types Result type
+ sign identity integer type integer type
real type real type
- sign negation integer type integer type
real type real type
Logical operators
The types of operands and results for logical operations are shown in the following
table.
Table 5-4 Logical operations
Operator Operation Operand types Result
type
not bitwise negation integer type Boolean
and bitwise and integer type Boolean
or bitwise or integer type Boolean
xor bitwise xor integer type Boolean
shl Operation integer type Boolean
shr Operation integer type Boolean
If the operand of the not operator is of an integer type, the result is of the same
integer type. The not operator is a unary operator.
If both operands of an and, or, or xor operator are of an integer type, the result type
is the common type of the two operands.
The operations I shl J and Ishr J shift the value of I to the left right by J bits. The
result type is the same as the type of I.
Boolean operators
The types of operands and results for Boolean operations are shown in the following
table.
Table 5-5 Boolean operations
Operator Operation Operand types Result type
not negation Boolean type Boolean
and logical and Boolean type Boolean
or logical or Boolean type Boolean
xor logical xor Boolean type Boolean
Normal Boolean logic governs the results of these operations. For instance, A and B
is True only if both A and B are True.
Chapter 5, Expressions 45
Object Pascal supports two different models of code generation for the and and or
operators: complete evaluation and short-circuit (partial) evaluation.
Complete evaluation means that every operand of a Boolean expression built from
the and and or operators is guaranteed to be evaluated, even when the result of the
entire expression is already known. This model is convenient when one or more
operands of an expression are functions with side effects that alter the meaning of
the program.
Short-circuit evaluation guarantees strict left-to-right evaluation and that evaluation
stops as soon as the result of the entire expression becomes evident. This model is
convenient in most cases because it guarantees minimum execution time, and
usually minimum code size. Short-circuit evaluation also makes possible the
evaluation of constructs that would not otherwise be legal. For example,
while (I <= Length(S)) and (S[I] <> ' ') do
Inc(I);
while (P <> nil) and (P^.Value <> 5) do
P := P^.Next;
In both cases, the second test isn’t evaluated if the first test is False.
The evaluation model is controlled through the $B compiler directive. The default
state is {$B-}, and in this state, the compiler generates short-circuit evaluation code.
In the {$B+} state, the compiler generates complete evaluation.
Because Standard Pascal doesn’t specify which model should be used for Boolean
expression evaluation, programs dependent on either model aren’t truly portable.
You may decide, however, that sacrificing portability is worth the gain in execution
speed and simplicity provided by the short-circuit model.
String operator
The types of operands and results for string operation are shown in the following
table.
Table 5-6 String operation
Operator Operation Operand types Result type
+ concatenation string type, Char type, string type
or packed string type
Object Pascal allows the + operator to be used to concatenate two string operands.
The result of the operation S + T, where S and T are of a string type, a Char type, or
a packed string type, is the concatenation of S and T. The result is compatible with
any string type (but not with Char types and packed string types). If the resulting
string is longer than 255 characters, it’s truncated after character 255.
Character-pointer operators
The extended syntax (enabled using a {$X+} compiler directive) supports a number
of character-pointer operations. The plus (+) and minus (-) operators can be used to
increment and decrement the offset part of a pointer value, and the minus operator
Set operators
The types of operands for set operations are shown in the following table.
Table 5-8 Set operations
Operator Operation Operand types
+ union compatible set types
- difference compatible set types
* intersection compatible set types
Relational operators
The types of operands and results for relational operations are shown in the
following table.
Table 5-9 Relational operations
Operator type Operation Operand types Result type
= equal compatible simple, class, class reference, Boolean
pointer, set, string, or packed string
Chapter 5, Expressions 47
types
<> not equal compatible simple, class, class reference, Boolean
pointer, set, string, or packed string
types
< less than compatible simple, string, packed string Boolean
types, or PChar
> greater than compatible simple, string, packed string Boolean
types, or PChar
<= less than or equal to compatible simple, string, packed string Boolean
types, or PChar
>= greater than or compatible simple, string, or packed Boolean
equal to string types, or PChar
<= subset of compatible set types Boolean
>= superset of compatible set types Boolean
in member of left operand, any ordinal type T; right Boolean
operand, set whose base is compatible
with T
Comparing strings
The relational operators =, <>, <, >>, >=, and <= compare strings according to the
ordering of the extended ASCII character set. Any two string values can be
compared because all string values are compatible.
A character-type value is compatible with a string-type value. When the two are
compared, the character-type value is treated as a string-type value with length 1.
When a packed string-type value with N components is compared with a string-
type value, it’s treated as a string-type value with length N.
Comparing sets
If A and B are set operands, their comparisons produce these results:
• A = B is True only if A and B contain exactly the same members; otherwise, A <>
B.
• A <= B is True only if every member of A is also a member of B.
• A >= B is True only if every member of B is also a member of A.
Class operators
Object Pascal defines two operators, is and as, that operate on class and object
references. See Chapter 9, “Class types.”
The @ operator
The @ operator is used in an address factor to compute the address of a variable,
procedure, function, or method.
address factor @ variable reference
procedure identifier
function identifier
qualified method identifier
The @ operator returns the address of its operand, that is, it constructs a pointer
value that points to the operand.
@ with a variable
When applied to a variable reference, @ returns a pointer to the variable. The type of
the resulting pointer value is controlled through the $T compiler directive: In the
{$T-} state (the default), the result type is Pointer. In other words, the result is an
untyped pointer, which is compatible with all other pointer types. In the {$T+} state,
the type of the result is ^T, where T is the type of the variable reference. In other
words, the result is of a type that is compatible only with other pointers to the type
of the variable.
Special rules apply to use of the @ operator with a procedural variable. For more
details, see “Procedural types in expressions” on page 52.
Chapter 5, Expressions 49
@ with a procedure, function, or method
You can apply @ to a procedure, function, or method to produce a pointer to the
routine’s entry point. The type of the resulting pointer is always Pointer, regardless
of the state of the $T compiler directive. In other words, the result is always an
untyped pointer, which is compatible with all other pointer types.
When @ is applied to a method, the method must be specified through a qualified-
method identifier (a class-type identifier, followed by a period, followed by a method
identifier).
Function calls
A function call activates a function specified by a function identifier, a method
designator, a qualified-method designator, or a procedural-type variable reference.
The function call must have a list of actual parameters if the corresponding function
declaration contains a list of formal parameters. Each parameter takes the place of
the corresponding formal parameter according to parameter rules explained in
Chapter 8, “Procedures and functions,” on page 75.
function call
function identifier
actual parameter list
method designator
qualified method designator
variable reference
Set constructors
A set constructor denotes a set-type value, and is formed by writing expressions
within brackets ([]). Each expression denotes a value of the set.
The notation [ ] denotes the empty set, which is assignment-compatible with every
set type. Any member group X..Y denotes as set members all values in the range
X..Y. If X is greater than Y, then X..Y doesn’t denote any members and [X..Y]
denotes the empty set.
All expression values in member groups in a particular set constructor must be of
the same ordinal type.
These are some examples of set constructors:
[red, C, green]
[1, 5, 10..K mod 12, 23]
['A'..'Z', 'a'..'z', Chr(Digit + 48)]
Value typecasts
The type of an expression can be changed to another type through a value typecast.
value typecast type identifier ( expression )
The expression type and the specified type must both be either ordinal types or
pointer types. For ordinal types, the resulting value is obtained by converting the
expression. The conversion may involve truncation or extension of the original
value if the size of the specified type is different from that of the expression. In cases
where the value is extended, the sign of the value is always preserved; that is, the
value is sign-extended.
The syntax of a value typecast is almost identical to that of a variable typecast.
Value typecasts operate on values, however, not on variables, and therefore they
can’t participate in variable references; that is, a value typecast can’t be followed by
qualifiers. In particular, value typecasts can’t appear on the left side of an
assignment statement. See “Variable typecasts” on page 34.
These are some examples of value typecasts:
Integer('A')
Char(48)
Boolean(0)
Color(2)
Longint(@Buffer)
BytePtr(Ptr($40, $49))
Chapter 5, Expressions 51
Procedural types in expressions
Usually, using a procedural variable in a statement or an expression calls the
procedure or function stored in the variable. There is one exception: When the
compiler sees a procedural variable on the left side of an assignment statement, it
knows that the right side has to represent a procedural value. For example, consider
the following program:
type
IntFunc = function: Integer;
var
F: IntFunc;
N: Integer;
function ReadInt: Integer; far;
var
I: Integer;
begin
Read(I);
ReadInt := I;
end;
begin
F := ReadInt; { Assign procedural value }
N := ReadInt; { Assign function result }
end.
The first statement in the main program assigns the procedural value (address of)
ReadInt to the procedural variable F, where the second statement calls ReadInt and
assigns the returned value to N. The distinction between getting the procedural
value or calling the function is made by the type of the variable being assigned (F or
N).
Unfortunately, there are situations where the compiler can’t determine the desired
action from the context. For example, in the following statement there is no obvious
way the compiler can know if it should compare the procedural value in F to the
procedural value of ReadInt to determine if F currently points to ReadInt, or if it
should call F and ReadInt and then compare the returned values.
if F = ReadInt then
Edit1.Text := 'Equal';
Object Pascal syntax, however, specifies that the occurrence of a function identifier
in an expression denotes a call to that function, so the effect of the preceding
statement is to call F and ReadInt, and then compare the returned values. To
compare the procedural value in F to the procedural value of ReadInt, the following
construct must be used:
if @F = @ReadInt then
Edit1.Text := 'Equal';
When applied to a procedural variable or a procedure or function identifier, the
address (@) operator prevents the compiler from calling the procedure, and at the
Chapter 5, Expressions 53
54 Object Pascal Language Guide
Chapter
Statements
6
Statements describe algorithmic actions that can be executed. Labels can prefix
statements, and these labels can be referenced by goto statements.
statement
label : simple statement
structured statement
Simple statements
A simple statement is a statement that doesn’t contain any other statements.
simple statement assignment statement
procedure statement
goto statement
Assignment statements
Assignment statements replace the current value of a variable with a new value
specified by an expression. They can be used to set the return value of the function
also.
assignment statement
variable reference := expression
function identifier
Chapter 6, Statements 55
The expression must be assignment-compatible with the type of the variable or the
type of the function result. See the section “Type compatibility” on page 25.
Some examples of assignment statements follow:
X := Y + Z;
Done := (I >= 1) and (I < 100);
Hue1 := [Blue, Succ(C)];
I := Sqr(J) - I * K;
Procedure statements
A procedure statement activates a procedure specified by a procedure identifier, a
method designator, a qualified-method designator, or a procedural-type variable
reference. If the corresponding procedure declaration contains a list of formal
parameters, then the procedure statement must have a matching list of (parameters
listed in definitions are formal parameters; in the calling statement, they are actual
parameters). The actual parameters are passed to the formal parameters as part of the
call. For more information, see Chapter 8, “Procedures and functions.”
procedure statement
procedure identifier
actual parameter list
method designator
qualified method designator
variable reference
Goto statements
A goto statement transfers program execution to the statement marked by the
specified label. The syntax diagram of a goto statement follows:
goto statement goto label
Structured statements
Structured statements are constructs composed of other statements that are to be
executed in sequentially (compound and with statements), conditionally
(conditional statements), or repeatedly (repetitive statements).
structured statement compound statement
conditional statement
repetitive statement
with statement
exception statement
Compound statements
The compound statement specifies that its component statements are to be executed
in the same sequence as they are written. The component statements are treated as
one statement, crucial in contexts where the Object Pascal syntax only allows one
statement. begin and end bracket the statements, which are separated by
semicolons.
compound statement begin statement end
;
Conditional statements
A conditional statement selects for execution a single one (or none) of its component
statements.
conditional statement if statement
case statement
If statements
The syntax for an if statement reads like this:
Chapter 6, Statements 57
if statement
if expression then statement
else statement
The expression must yield a result of the standard type Boolean. If the expression
produces the value True, then the statement following then is executed.
If the expression produces False and the else part is present, the statement following
else is executed; if the else part isn’t present, execution continues at the next
statement following the if statement.
The syntactic ambiguity arising from the construct
if e1 then if e2 then s1 else s2;
is resolved by interpreting the construct as follows:
Note
No semicolon is allowed preceding an else clause.
if e1 then
begin
if e2 then
s1
else
s2
end;
Usually, an else is associated with the closest if not already associated with an else.
Two examples of if statements follow:
if X < 1.5 then
Z := X + Y
else
Z := 1.5;
Case statements
The case statement consists of an expression (the selector) and a list of statements,
each prefixed with one or more constants (called case constants) or with the word
else. The selector must be of a byte-sized or word-sized ordinal type, so string types
and the integer type Longint are invalid selector types. All case constants must be
unique and of an ordinal type compatible with the selector type.
case statement case expression of case
;
end
else part ;
The case statement executes the statement prefixed by a case constant equal to the
value of the selector or a case range containing the value of the selector. If no such
case constant of the case range exists and an else part is present, the statement
following else is executed. If there is no else part, execution continues with the next
statement following the if statement.
These are examples of case statements:
case Operator of
Plus: X := X + Y;
Minus: X := X - Y;
Times: X := X * Y;
end;
case I of
0, 2, 4, 6, 8: Edit1.Text := 'Even digit';
1, 3, 5, 7, 9: Edit1.Text := 'Odd digit';
10..100: Edit1.Text := 'Between 10 and 100';
else
Edit1.Text := ’Negative or greater than 100';
end;
Ranges in case statements must not overlap. So for example, the following case
statement is not allowed:
case MySelector of
5: Edit1.Text := 'Special case';
1..10: Edit1.Text := 'General case';
end;
Repetitive statements
Repetitive statements specify certain statements to be executed repeatedly.
repetitive statement repeat statement
while statement
for statement
Chapter 6, Statements 59
Repeat statements
A repeat statement contains an expression that controls the repeated execution of a
statement sequence within that repeat statement.
repeat statement
repeat statement until expression
;
The expression must produce a result of type Boolean. The statements between the
symbols repeat and until are executed in sequence until, at the end of a sequence,
the expression yields True. The sequence is executed at least once because the
expression is evaluated after the execution of each sequence.
These are examples of repeat statements:
repeat
K := I mod J;
I := J;
J := K;
until J = 0;
repeat
Write('Enter value (0..9): ');
Readln(I);
until (I >= 0) and (I <= 9);
While statements
A while statement contains an expression that controls the repeated execution of a
statement (which can be a compound statement).
while statement while expression do statement
while I > 0 do
begin
if Odd(I) then Z := Z * X;
I := I div 2;
X := Sqr(X);
end;
while not Eof(InFile) do
begin
Readln(InFile, Line);
Process(Line);
For statements
The for statement causes a statement to be repeatedly executed while a progression
of values is assigned to a control variable. Such a statement can be a compound
statement.
for statement for control variable := initial value
to
final value do statement
downto
The control variable must be a variable identifier (without any qualifier) that is local
in scope to the block containing the for statement. The control variable must be of an
ordinal type. The initial and final values must be of a type assignment-compatible
with the ordinal type. See Chapter 7 for a discussion of locality and scope.
When a for statement is entered, the initial and final values are determined once for
the remainder of the execution of the for statement.
The statement contained by the for statement is executed once for every value in the
range initial value to final value. The control variable always starts off at initial value.
When a for statement uses to, the value of the control variable is incremented by one
for each repetition. If initial value is greater than final value, the contained statement
isn’t executed. When a for statement uses downto, the value of the control variable
is decremented by one for each repetition. If initial value value is less than final value,
the contained statement isn’t executed.
If the contained statement alters the value of the control variable, your results will
probably not be what you expect. After a for statement is executed, the value of the
control variable value is undefined, unless execution of the for statement was
interrupted by a goto from the for statement.
With these restrictions in mind, the for statement
for V := Expr1 to Expr2 do Body;
is equivalent to
begin
Temp1 := Expr1;
Temp2 := Expr2;
if Temp1 <= Temp2 then
begin
V := Temp1;
Body;
while V <> Temp2 do
Chapter 6, Statements 61
begin
V := Succ(V);
Body;
end;
end;
end;
and the for statement
for V := Expr1 downto Expr2 do Body;
is equivalent to
begin
Temp1 := Expr1;
Temp2 := Expr2;
if Temp1 >= Temp2 then
begin
V := Temp1;
Body;
while V <> Temp2 do
begin
V := Pred(V);
Body;
end;
end;
end;
where Temp1 and Temp2 are auxiliary variables of the host type of the variable V
and don’t occur elsewhere in the program.
These are examples of for statements:
for I := 2 to 63 do
if Data[I] > Max then
Max := Data[I]
for I := 1 to 10 do
for J := 1 to 10 do
begin
X := 0;
for K := 1 to 10 do
X := X + Mat1[I, K] * Mat2[K, J];
Mat[I, J] := X;
end;
With statements
The with statement is shorthand for referencing the fields of a record, and the fields
and methods of an object. Within a with statement, the fields of one or more specific
record variables can be referenced using their field identifiers only. The syntax of a
with statement follows:
record or object
variable reference variable reference
Chapter 6, Statements 63
with X do
begin
X := 10;
Y := 25;
end;
the X between with and do refers to the variable of type TPoint, but in the
compound statement, X and Y refer to X.X and X.Y.
The statement
with V1, V2, ... Vn do S;
is equivalent to
with V1 do
with V2 do
ƒ
with Vn do
S;
In both cases, if Vn is a field of both V1 and V2, it’s interpreted as V2.Vn, not V1.Vn.
If the selection of a record variable involves indexing an array or dereferencing a
pointer, these actions are executed once before the component statement is
executed.
Blocks
The overall syntax of any block follows this format:
block declaration part statement part
declaration part
label declaration part
constant declaration part
type declaration part
variable declaration part
procedure/function declaration part
exports clause
Labels that mark statements in the corresponding statement part are declared in the
label declaration part. Each label must mark only one statement.
label declaration part label label ;
,
The type declaration part includes all type declarations local to the block.
type declaration part type type declaration
The variable declaration part is composed of variable declarations local to the block.
variable declaration part var variable declaration
The procedure and function declarations local to the block make up the procedure
and function declaration part.
procedure/function declaration part
procedure declaration
function declaration
constructor declaration
destructor declaration
The exports clause lists all procedures and functions that are exported by the
current program or dynamic-link library. An exports clause is allowed only in the
outermost declaration part of a program or dynamic-link library—it isn’t allowed in
the declaration part of a procedure, function, or unit. See “The exports clause” on
page 135.
The statement part defines the statements or algorithmic actions to be executed by the
block.
statement part compound statement
Rules of scope
The presence of an identifier or label in a declaration defines the identifier or label.
Each time the identifier or label occurs again, it must be within the scope of this
declaration.
Block scope
The scope of an identifier or label declared in a label, constant, type, variable,
procedure, or function declaration stretches from the point of declaration to the end
of the current block, and includes all blocks enclosed by the current block.
An identifier or label declared in an outer block can be redeclared in an inner block
enclosed by the outer block. Before the point of declaration in the inner block, and
Record scope
The scope of a field identifier declared in a record-type definition extends from the
point of declaration to the end of the record-type definition. Also, the scope of field
identifiers includes field designators and with statements that operate on variable
references of the given record type. See “Record types” on page 19.
Class scope
The scope of a component identifier declared in a class type extends from the point
of declaration to the end of the class-type definition, and extends over all
descendants of the class type and the blocks of all method declarations of the class
type. Also, the scope of component identifiers includes field, method, and property
designators, and with statements that operate on variables of the given class type.
For more information on classes, see Chapter 9, Class Types.
Unit scope
The scope of identifiers declared in the interface section of a unit follows the rules of
block scope and extends over all clients of the unit. In other words, programs or
units containing uses clauses have access to the identifiers belonging to the interface
parts of the units in those uses clauses.
Each unit in a uses clause imposes a new scope that encloses the remaining units
used and the program or unit containing the uses clause. The first unit in a uses
clause represents the outermost scope, and the last unit represents the innermost
scope. This implies that if two or more units declare the same identifier, an
unqualified reference to the identifier selects the instance declared by the last unit in
Procedure declarations
A procedure declaration associates an identifier with a block as a procedure; that
procedure can then be activated by a procedure statement.
procedure declaration
procedure heading ; subroutine block ;
procedure heading
procedure identifier
qualified method identifier
The procedure heading names the procedure’s identifier and specifies the formal
parameters (if any). The syntax for a formal parameter list is shown in the section
“Parameters” on page 75.
A procedure is activated by a procedure statement, which states the procedure’s
identifier and actual parameters, if any. The statements to be executed on activation
are noted in the statement part of the procedure’s block. If the procedure’s identifier
is used in a procedure statement within the procedure’s block, the procedure is
executed recursively (it calls itself while executing).
Here’s an example of a procedure declaration:
procedure NumString(N: Integer; var S: string);
var
V: Integer;
begin
V := Abs(N);
S := '';
repeat
S := Chr(N mod 10 + Ord('0')) + S;
N := N div 10;
until N = 0;
if N < 0 then S := '-' + S;
end;
Export declarations
The export directive makes a procedure or function exportable by forcing the
routine to use the far call model and generating special procedure entry and exit
code.
Procedures and functions must be exportable in these cases:
• Procedures and functions that are exported by a DLL (dynamic-link library)
• Callback procedures and functions in a Windows program
Chapter 12, “Dynamic-link libraries,” discusses how to export procedures and
functions in a DLL. Even though a procedure or function is compiled with an export
directive, the actual exporting of the procedure or function doesn’t occur until the
routine is listed in a library’s exports clause.
Callback procedures and functions are routines in your application that are called
by Windows and not by your application itself. Callback routines must be compiled
with the export directive, but they don’t have to be listed in an exports clause. Here
are some examples of common callback procedures and functions:
• Window procedures
• Dialog procedures
• Enumeration callback procedures
• Memory-notification procedures
• Window-hook procedures (filters)
Object Pascal automatically generates smart callbacks for procedures and functions
that are exported by a Windows program. Smart callbacks alleviate the need to use
the MakeProcInstance and FreeProcInstance Windows API routines when creating
callback routines. See “Entry and exit code” on page 176.
cdecl declarations
The cdecl directive specifies that a procedure or function should use C calling
conventions. C calling conventions differ from Pascal calling conventions in that
parameters are pushed on the stack in reverse order, and that the caller (as opposed
to the callee) is responsible for removing the parameters from the stack after the call.
The cdecl directive is useful for interfacing with dynamic-link libraries written in C
Forward declarations
A procedure or function declaration that specifies the directive forward instead of a
block is a forward declaration. Somewhere after this declaration, the procedure
must be defined by a defining declaration. The defining declaration can omit the
formal parameter list and the function result, or it can optionally repeat it. In the
latter case, the defining declaration’s heading must match exactly the order, types,
and names of parameters, and the type of the function result, if any.
No forward declarations are allowed in the interface part of a unit.
The forward declaration and the defining declaration must appear in the same
procedure and function declaration part. Other procedures and functions can be
declared between them, and they can call the forward-declared procedure.
Therefore, mutual recursion is possible.
The forward declaration and the defining declaration constitute a complete
procedure or function declaration. The procedure or function is considered declared
at the forward declaration.
This is an example of a forward declaration:
procedure Walter(M, N: Integer); forward;
External declarations
With external declarations, you can interface with separately compiled procedures
and functions written in assembly language. They also allow you to import
procedures and functions from DLLs.
External directives consisting only of the reserved word external are used in
conjunction with {$L filename} directives to link with external procedures and
functions implemented in .OBJ files. For more details about linking with assembly
language, see Chapter 20.
These are examples of external procedure declarations:
procedure MoveWord(var Source, Dest; Count: Word); external;
procedure MoveLong(var Source, Dest; Count: Word); external;
procedure FillWord(var Dest; Data: Integer; Count: Word); external;
procedure FillLong(var Dest; Data: Longint; Count: Word); external;
{$L BLOCK.OBJ}
External directives that specify a dynamic-link library name (and optionally an
import name or an import ordinal number) are used to import procedures and
functions from dynamic-link libraries. For example, this external declaration
imports a function called GlobalAlloc from the DLL called KERNEL (the Windows
kernel):
function GlobalAlloc(Flags: Word; Bytes: Longint): THandle; far;
external 'KERNEL' index 15;
To read more about importing procedures and functions from a DLL, see Chapter
12.
The external directive takes the place of the declaration and statement parts in an
imported procedure or function. Imported procedures and functions must use the
far call model selected by using a far procedure directive or a {$F+} compiler
directive. Aside from this requirement, imported procedures and functions are just
like regular procedures and functions.
Assembler declarations
With assembler declarations, you can write entire procedures and functions in
inline assembly language.
asm block
assembler ; declaration part asm statement
For more details on assembler procedures and functions, see Chapter 19.
Function declarations
A function declaration defines a block that computes and returns a value.
function declaration
function heading ; subroutine block ;
function heading
function identifier
formal parameter list
qualified method identifier
: result type
The function heading specifies the identifier for the function, the formal parameters
(if any), and the function result type.
A function is activated by the evaluation of a function call. The function call gives
the function’s identifier and actual parameters, if any, required by the function. A
function call appears as an operand in an expression. When the expression is
evaluated, the function is executed, and the value of the operand becomes the value
returned by the function.
The statement part of the function’s block specifies the statements to be executed
upon activation of the function. The block should contain at least one assignment
statement that assigns a value to the function identifier. The result of the function is
the last value assigned. If no such assignment statement exists or if it isn’t executed,
the value returned by the function is undefined.
Parameters
The declaration of a procedure or function specifies a formal parameter list. Each
parameter declared in a formal parameter list is local to the procedure or function
being declared and can be referred to by its identifier in the block associated with
the procedure or function.
formal parameter list ( parameter declaration )
;
identifier list
var : parameter type
const array of
There are four kinds of parameters: value, constant, variable, and untyped. These are
characterized as follows:
• A parameter group without a preceding var and followed by a type is a list of
value parameters.
• A parameter group preceded by const and followed by a type is a list of constant
parameters.
• A parameter group preceded by var and followed by a type is a list of variable
parameters.
• A parameter group preceded by var or const and not followed by a type is a list
of untyped parameters.
String and array-type parameters can be open parameters. Open parameters are
described on page 78. A variable parameter declared using the OpenString identifier,
or using the string keyword in the {$P+} state, is an open-string parameter. A value,
constant, or variable parameter declared using the syntax array of T is an open-array
parameter.
Value parameters
A formal value parameter acts like a variable local to the procedure or function,
except it gets its initial value from the corresponding actual parameter upon
activation of the procedure or function. Changes made to a formal value parameter
don’t affect the value of the actual parameter.
A value parameter’s corresponding actual parameter in a procedure statement or
function call must be an expression, and its value must not be of file type or of any
structured type that contains a file type.
The actual parameter must be assignment-compatible with the type of the formal
value parameter. If the parameter type is string, then the formal parameter is given
a size attribute of 255.
Constant parameters
A formal constant parameter acts like a local read-only variable that gets its value
from the corresponding actual parameter upon activation of the procedure or
function. Assignments to a formal constant parameter are not allowed, and likewise
a formal constant parameter can’t be passed as an actual variable parameter to
another procedure or function.
A constant parameter’s corresponding actual parameter in a procedure statement or
function must follow the same rules as an actual value parameter.
Variable parameters
A variable parameter is used when a value must be passed from a procedure or
function to the caller. The corresponding actual parameter in a procedure statement
or function call must be a variable reference. The formal variable parameter
represents the actual variable during the activation of the procedure or function, so
any changes to the value of the formal variable parameter are reflected in the actual
parameter.
Within the procedure or function, any reference to the formal variable parameter
accesses the actual parameter itself. The type of the actual parameter must be
identical to the type of the formal variable parameter (you can bypass this
restriction through untyped parameters).
Note File types can be passed only as variable parameters.
The $P compiler directive controls the meaning of a variable parameter declared
using the string keyword. In the default {$P+} state, string indicates that the
parameter is an open-string parameter. In the {$P-} state, string corresponds to a
string type with a size attribute of 255. See page 78 for information on open-string
parameters.
If referencing an actual variable parameter involves indexing an array or finding the
object of a pointer, these actions are executed before the activation of the procedure
or function.
Untyped parameters
When a formal parameter is an untyped parameter, the corresponding actual
parameter can be any variable or constant reference, regardless of its type. An
untyped parameter declared using the var keyword can be modified, whereas an
untyped parameter declared using the const keyword is read-only.
Within the procedure or function, the untyped parameter is typeless; that is, it is
incompatible with variables of all other types, unless it is given a specific type
through a variable typecast.
This is an example of untyped parameters:
function Equal(var Source, Dest; Size: Word): Boolean;
type
TBytes = array[0..65534] of Byte;
var
N: Word;
begin
Open parameters
Open parameters allow strings and arrays of varying sizes to be passed to the same
procedure or function.
Open-string parameters
Open-string parameters can be declared in two ways:
• Using the string keyword in the {$P+} state
• Using the OpenString identifier
By default, parameters declared with the string keyword are open-string
parameters. If, for reasons of backward compatibility, a procedure or function is
compiled in the {$P-} state, the OpenString identifier can be used to declare open-
string parameters. OpenString is declared in the System unit and denotes a special
string type that can only be used in the declaration of string parameters. OpenString
is not a reserved word; therefore, OpenString can be redeclared as a user-defined
identifier.
Open-array parameters
A formal parameter declared using the syntax
array of T
Open-array constructors
Open-array constructors allow open-array parameters to be constructed directly
within procedure and function calls. When a formal parameter of a procedure or
function is an open-array value parameter or an open-array constant parameter, the
corresponding actual parameter in a procedure or function call can be an open-array
constructor.
open array constructor
[ expression ]
,
Class types
9
A class type is a structure consisting of a fixed number of components. The possible
components of a class are fields, methods, and properties. Unlike other types, a class
type can be declared only in a type declaration part in the outermost scope of a
program or unit. Therefore, a class type can't be declared in a variable declaration
part or within a procedure, function, or method block.
object type
class
heritage component list end
visibility specifier
visibility specifier
published
public
protected
private
component list
property definition
field definition
identifier list : type ;
method definition
method heading ; method directives
constructor heading
destructor heading
method directives
virtual ;
dynamic
Fields
A field declaration in a class defines a data item that exists in each instance of the
class. This is similar to a field of a record.
Methods
A method is a procedure or function that performs an operation on an object. Part of
the call to a method specifies the object the method should operate on.
The declaration of a method within a class type corresponds to a forward
declaration of that method. This means that somewhere after the class-type
declaration, and within the same module, the method must be implemented by a
defining declaration.
Within the implementation of a method, the identifier Self represents an implicit
parameter that references the object for which the method was invoked.
Constructors and destructors are special methods that control construction and
destruction of objects.
A constructor defines the actions associated with creating an object. When invoked,
a constructor acts as a function that returns a reference to a newly allocated and
initialized instance of the class type.
A destructor defines the actions associated with destroying an object. When
invoked, a destructor will deallocate the memory that was allocated for the object.
A class method is a procedure or function that operates on a class reference instead of
an object reference.
Properties
A property declaration in a class defines a named attribute for objects of the class
and the actions associated with reading and writing the attribute. Properties are
described in depth beginning on page 101
Inheritance
A class type can inherit components from another class type. If T2 inherits from T1,
then T2 is a descendant of T1, and T1 is an ancestor of T2. Inheritance is transitive; that
is, if T3 inherits from T2, and T2 inherits from T1, then T3 also inherits from T1. The
domain of a class type consists of itself and all of its descendants.
A descendant class implicitly contains all the components defined by its ancestor
classes. A descendant class can add new components to those it inherits. However,
it can’t remove the definition of a component defined in an ancestor class.
Forward references
The declaration of a class type can specify the reserved word class and nothing else,
in which case the declaration is a forward declaration. A forward declaration must
be resolved by a normal declaration of the class within the same type declaration
part. Forward declarations allow mutually dependent classes to be declared. For
example:
type
TFigure = class;
TDrawing = class
Figure: TFigure;
:
end;
TFigure = class
Drawing: TDrawing;
:
end;
Component visibility
The visibility of a component identifier is governed by the visibility attribute of the
component section that declares the identifier. The four possible visibility attributes
are published, public, protected, and private.
Component identifiers declared in the component list that immediately follows the
class type heading have the published visibility attribute if the class type is compiled
in the {$M+} state or is derived from a class that was compiled in the {$M+} state.
Otherwise, such component identifiers have the public visibility attribute.
Public components
Component identifiers declared in public sections have no special restrictions on
their visibility.
Published components
The visibility rules for published components are identical to those of public
components. The only difference between published and public components is that
run-time type information is generated for fields and properties that are declared in a
published section. This run-time type information enables an application to
dynamically query the fields and properties of an otherwise unknown class type.
Note The Delphi Visual Component Library uses run-time type information to access the
values of a component's properties when saving a loading form files. Also, the
Delphi development environment uses a component's run-time type information to
determine the list of properties shown in the Object Inspector.
A class type cannot have published sections unless it is compiled in the {$M+} state
or is derived from a class that was compiled in the {$M+} state. The $M compiler
directive controls the generation of run-time type information for a class. For further
details on $M, see Appendix B.
Protected components
When accessing through a class type declared in the current module, the protected
component identifiers of the class and its ancestors are visible. In all other cases,
protected component identifiers are hidden.
Access to protected components of a class is restricted to the implementation of
methods of the class and its descendants. Therefore, components of a class that are
intended for use only in the implementation of derived classes are usually declared
as protected.
Private components
The visibility of a component identifier declared in a private component section is
restricted to the module that contains the class-type declaration. In other words,
private component identifiers act like normal public component identifiers within
the module that contains the class-type declaration, but outside the module, any
private component identifiers are unknown and inaccessible. By placing related
class types in the same module, these class types can gain access to each other's
private components without making the private components known to other
modules.
Static methods
Methods declared in a class type are by default static. When a static method is
called, the declared (compile-time) type of the class or object used in the method call
determines which method implementation to activate. In the following example, the
Draw methods are static:
type
TFigure = class
procedure Draw;
:
end;
TRectangle = class(TFigure)
procedure Draw;
:
end;
The following section of code illustrates the effect of calling a static method. Even
though in the second call to Figure.Draw the Figure variable references an object of
Virtual methods
A method can be made virtual by including a virtual directive in its declaration.
When a virtual method is called, the actual (run-time) type of the class or object used
in the method call determines which method implementation to activate. The
following is an example of a declaration of a virtual method:
type
TFigure = class
procedure Draw; virtual;
:
end;
A virtual method can be overridden in a descendant class. When an override
directive is included in the declaration of a method, the method overrides the
inherited implementation of the method. An override of a virtual method must
match exactly the order and types of the parameters, and the function result type (if
any), of the original method.
The only way a virtual method can be overridden is through the override directive.
If a method declaration in a descendant class specifies the same method identifier as
an inherited method, but doesn’t specify an override directive, the new method
declaration will hide the inherited declaration, but not override it.
Assuming the declaration of class TFigure above, the following two descendant
classes override the Draw method:
type
TRectangle = class(TFigure)
procedure Draw; override;
:
end;
TEllipse = class(TFigure)
procedure Draw; override;
Dynamic methods
A method is made dynamic by including a dynamic directive in its declaration.
Dynamic methods are semantically identical to virtual methods. Virtual and
dynamic methods differ only in the implementation of method call dispatching at
run time; for all other purposes, the two types of methods can be considered
equivalent.
In the implementation of virtual methods, the compiler favors speed of call
dispatching over code size. The implementation of dynamic methods on the other
hand favors code size over speed of call dispatching.
In general, virtual methods are the most efficient way to implement polymorphic
behavior. Dynamic methods are useful only in situations where a base class declares
a large number of virtual methods, and an application declares a large number of
descendant classes with few overrides of the inherited virtual methods.
Abstract methods
An abstract method is a virtual or dynamic method whose implementation isn’t
defined in the class declaration in which it appears; its definition is instead deferred
to descendant classes. An abstract method in effect defines an interface, but not the
underlying operation.
A method is abstract if an abstract directive is included in its declaration. A method
can be declared abstract only if it is first declared virtual or dynamic. The following
is an example of a declaration of an abstract method.
type
TFigure = class
procedure Draw; virtual; abstract;
:
end;
Method activations
A method is activated (or called) through a function call or procedure statement
consisting of a method designator followed by an actual parameter list. This type of
call is known as method activation.
method designator
method identifier
variable reference .
Method implementations
The declaration of a method within a class type corresponds to a forward
declaration of that method. Somewhere after the class-type declaration, and within
the same module, the method must be implemented by a defining declaration. For
example, given the class-type declaration
type
TFramedLabel = class(TLabel)
protected
procedure Paint; override;
end;
the Paint method must later be implemented by a defining declaration. For example,
procedure TFramedLabel.Paint;
begin
inherited Paint;
with Canvas do
begin
Brush.Color := clWindowText;
Brush.Style := bsSolid;
FrameRect(ClientRect);
constructor heading
constructor identifier
qualified method identifier
Destructors
Destructors are used to destroy objects. When a destructor is invoked, the user-
defined actions of the destructor are executed, and then the storage that was
allocated for the object is disposed of. The user-defined actions of a destructor
typically consist of destroying any embedded objects and releasing any resources
that were allocated by the object.
destructor declaration
destructor heading ; subroutine block ;
The following example shows how the destructor that was declared for the TShape
class in the preceding section might be implemented.
destructor TShape.Destroy;
begin
FBrush.Free;
FPen.Free;
inherited Destroy;
end;
The last action of a destructor is typically to call the inherited destructor to destroy
the inherited fields of the object.
While it is possible to declare multiple destructors for a class, it is recommended
that classes only implement overrides of the inherited Destroy destructor. Destroy is
a parameterless virtual destructor declared in TObject, and because TObject is the
ultimate ancestor of every class, the Destroy destructor always available for any
object.
As described in the preceding section on constructors, if an exception occurs during
the execution of a constructor, the Destroy destructor is invoked to destroy the
unfinished object. This means that destructors must be prepared to handle
destruction of partially constructed objects. Because a constructor sets all fields of a
new object to null values before executing any user defined actions, any class-type
or pointer-type fields in a partially constructed object are always nil. A destructor
should therefore always check for nil values before performing operations on class-
type or pointer-type fields.
Referring to the TShape.Destroy destructor mentioned earlier, note that the Free
method (which is inherited from TObject) is used to destroy the objects referenced
by the FPen and FBrush fields. The implementation of the Free method is
procedure TObject.Free;
begin
if Self <> nil then Destroy;
end;
The Free method is a convenient way of checking for nil before invoking Destroy on
an object reference. By calling Free instead of Destroy for any class-type fields, a
destructor is automatically prepared to handle partially constructed objects
resulting from constructor exceptions. For that same reason, direct calls to Destroy
aren’t recommended.
The is operator
The is operator is used to perform dynamic type checking. Using the is operator, you
can check whether the actual (run-time) type of an object reference belongs to a
particular class. The syntax of the is operator is
ObjectRef is ClassRef
where ObjectRef is an object reference and ClassRef is a class reference. The is
operator returns a boolean value. The result is True if ObjectRef is an instance of the
class denoted by ClassRef or an instance of a class derived from the class denoted by
ClassRef. Otherwise, the result is False. If ObjectRef is nil, the result is always False. If
the declared types of ObjectRef and ClassRef are known not to be relatedthat is if
the declared type of ObjectRef is known not to be an ancestor of, equal to, or a
descendant of ClassRefthe compiler reports a type-mismatch error.
The is operator is often used in conjunction with an if statement to perform a
guarded typecast. For example,
if ActiveControl is TEdit then TEdit(ActiveControl).SelectAll;
Here, if the is test is True, it is safe to typecast ActiveControl to be of class TEdit.
The rules of operator precedence group the is operator with the relational operators
(=, <>, <, >, <=, >=, and in). This means that when combined with other boolean
expressions using the and and or operators, is tests must be enclosed in
parentheses:
if (Sender is TButton) and (TButton(Sender).Tag <> 0) then ...;
The as operator
The as operator is used to perform checked typecasts. The syntax of the as operator is
ObjectRef as ClassRef
where ObjectRef is an object reference and ClassRef is a class reference. The resulting
value is a reference to the same object as ObjectRef, but with the type given by
ClassRef. When evaluated at run time, ObjectRef must be nil, an instance of the class
denoted by ClassRef, or an instance of a class derived from the class denoted by
ClassRef. If none of these conditions are True, an exception is raised. If the declared
types of ObjectRef and ClassRef are known not to be relatedthat is, if the declared
type of ObjectRef is known not to be an ancestor of, equal to, or a descendant of
ClassRefthe compiler reports a type-mismatch error.
The as operator is often used in conjunction with a with statement. For example,
with Sender as TButton do
begin
Caption := '&Ok';
Message handling
Message handler methods are used to implement user-defined responses to
dynamically dispatched messages. Delphi's Visual Class Library uses message handler
methods to implement Windows message handling.
Message dispatching
Message handler methods are typically not called directly. Instead, messages are
dispatched to an object using the Dispatch method defined in the TObject class.
Dispatch is declared as
procedure TObject.Dispatch(var Message);
The Message parameter passed to Dispatch must be a record, and the first entry in
the record must be a field of type Cardinal which contains the message ID of the
message being dispatched. For example
type
TMessage = record
Msg: Cardinal;
...
end;
A message record can contain any number of additional fields that define message
specific information.
A call to Dispatch invokes the most derived implementation of a message handler
for the given message ID. In other words, Dispatch invokes the first message handler
with a matching message ID found by examining the class itself, its ancestor, its
ancestor's ancestor, and so on until TObject is reached. If the class and its ancestors
do not define a handler for the given message ID, Dispatch will instead invoke the
DefaultHandler method.
The DefaultHandler method is declared in TObject as follows
procedure DefaultHandler(var Message); virtual;
The implementation of DefaultHandler in TObject simply returns without performing
any actions. By overriding DefaultHandler, a class can implement default handling of
messages. As described above, DefaultHandler is invoked when Dispatch is called to
Properties
A property definition in a class declares a named attribute for objects of the class
and the actions associated with reading and writing the attribute. Examples of
properties are the caption of a form, the size of a font, the name of a database table,
and so on.
Properties are a natural extension of fields in an object. Both can be used to express
attributes of an object, but whereas fields are merely storage locations which can be
examined and modified at will, properties provide greater control over access to
attributes, they provide a mechanism for associating actions with the reading and
writing of attributes, and they allow attributes to be computed.
property definition
property identifier property specifiers ;
property interface
property interface
: type identifier
property parameter list
property specifiers
read specifier
read field or method
write specifier
write field or method
stored specifier
stored field or method
boolean constant
nodefault
field or method
field identifier
method identifier
Property definitions
The definition of a property specifies the name and type of the property, and the
actions associated with reading (examining) and writing (modifying) the property.
A property can be of any type except a file type.
The declarations below define an imaginary TCompass control which has a Heading
property that can assume values from 0 to 359 degrees. The definition of the Heading
property further states that its value is read from the FHeading field, and that its
value is written using the SetHeading method.
type
THeading = 0..359;
TCompass = class(TControl)
private
FHeading: THeading;
procedure SetHeading(Value: THeading);
published
property Heading: THeading read FHeading write SetHeading;
:
end;
Property access
When a property is referenced in an expression, its value is read using the field or
method listed in the read specifier, and when a property is referenced in an
assignment statement, its value is written using the field or method listed in the
write specifier. For example, assuming that Compass is an object reference of the
TCompass type defined above, the statements
if Compass.Heading = 180 then GoingSouth;
Compass.Heading := 135;
correspond to
if Compass.FHeading = 180 then GoingSouth;
Compass.SetHeading(135);
In the TCompass class, no action is associated with reading the Heading property. The
read operation simply consists of examining the value stored in the FHeading field.
On the other hand, assigning a value to the Heading property translates into a call to
the SetHeading method, which not only stores the new value in the FHeading field,
Access specifiers
The read and write specifiers of a property definition control how a property is
accessed. A property definition must include at least a read or a write specifier, but
isn’t required to include both. If a property definition includes only a read specifer,
then the property is said to be read-only property, and if a property definition
includes only a write specifier, then the property is said to be write-only property. If a
property definition includes both a read and a write specifier, the property is said to
be read-write property. It is an error to assign a value to a read-only property.
Likewise, it is an error to use a write-only property in an expression.
The read or write keyword in an access specifier must be followed by a field
identifier or a method identifier. The field or method can belong to the class type in
which the property is defined, in which case the field or method definition must
precede the property definition, or it can belong to an ancestor class, in which case
the field or method must be visible in the class containing the property definition.
Fields and methods listed in access specifiers are governed by the following rules:
• If an access specifier lists a field identifier, then the field type must be identical to
the property type.
• If a read specifier lists a method identifier, then the method must be a
parameterless function method, and the function result type must be identical to
the property type.
• If a write specifier lists a method identifier, then the method must be a procedure
method that takes a single value or constant parameter of the same type as the
property type.
For example, if a property is defined as
property Color: TColor read GetColor write SetColor;
then preceding the property definition, the GetColor method must be defined as
function GetColor: TColor;
Array properties
Array properties allow the implementation of indexed properties. Examples of array
properties include the items of a list, the child controls of a control, and the pixels of
a bitmap.
The definition of an array property includes an index parameter list which specifies
the names and types of the indexes of the array property. For example,
property Objects[Index: Integer]: TObject
read GetObject write SetObject;
property Pixels[X, Y: Integer]: TColor
read GetPixel write SetPixel;
property Values[const Name: string]: string
read GetValue write SetValue;
The format of an index parameter list is the same as that of a procedure or function's
formal parameter list, except that the parameter declarations are enclosed in square
brackets instead of parentheses. Note that unlike array types, which can only specify
ordinal type indexes, array properties allow indexes of any type. For example, the
Values property declared previously might represent a lookup table in which a
string index is used to look up a string value.
An access specifier of an array property must list a method identifier. In other
words, the read and write specifiers of an array property aren’t allowed to specify a
field name. The methods listed in array property access specifiers are governed by
the following rules:
• The method listed in the read specifier of an array property must be a function
that takes the same number and types of parameters as are listed in the property's
index parameter list, and the function result type must be identical to the
property type.
• The method listed in the write specifier of an array property must be a procedure
with the same number and types of parameters as are listed in the property's
index parameter list, plus an additional value or constant parameter of the same
type as the property type.
For example, for the array properties declared above, the access methods might be
declared as
function GetObject(Index: Integer): TObject;
function GetPixel(X, Y: Integer): TColor;
function GetValue(const Name: string): string;
procedure SetObject(Index: Integer; Value: TObject);
procedure SetPixel(X, Y: Integer; Value: TColor);
procedure SetValue(const Name, Value: string);
Index specifiers
The definition of a property can optionally include an index specifier. Index specifiers
allow a number of properties to share the same access methods. An index specifier
consists of the directive index followed by an integer constant with a value between
–32767 and 32767. For example
type
TRectangle = class
private
FCoordinates: array[0..3] of Longint;
function GetCoordinate(Index: Integer): Longint;
procedure SetCoordinate(Index: Integer; Value: Longint);
public
Storage specifiers
The optional stored, default, and nodefault specifiers of a property definition are
called storage specifiers. They control certain aspects of the run-time type information
that is generated for published properties. Storage specifiers are only supported
for normal (non-array) properties.
Storage specifiers have no semantic effects on a property, that is they do not affect
how a property is used in program code. The Delphi Visual Component Library,
however, uses the information generated by storage specifiers to control filing of a
component's propertiesthe automatic saving and loading of a component's
property values in a form file. The stored directive controls whether a property is
filed, and the default and nodefault properties control the value that is considered a
property's default value.
If present in a property definition, the stored keyword must be followed by a
boolean constant (True or False), the identifier of a field of type Boolean, or the
identifier of a parameterless function method with returns a value of type Boolean. If
a property definition doesn’t include a stored specifier, the results are the same as if
a stored True specifier were included.
Property overrides
A property definition that doesn’t include a property interface is called a property
override. A property override allows a derived class to change the visibility, access
specifiers, and storage specifiers of an inherited property.
In its simplest form, a property override specifies only the reserved word property
followed by an inherited propery identifier. This form is used to change the
visibility of a property. If, for example, a base class defines a property in a protected
section, a derived class can raise the visibility of the property by declaring a
property override in a public or published section.
A property override can include a read, write, stored, and default or nodefault
specifier. Any such specifier overrides the corresponding inherited specifier. Note
that a property override can change an inherited access specifier or add a missing
access specifier, but it can’t remove an access specifier.
The following declarations illustrate the use of property overrides to change the
visibility, access specifiers, and storage specifiers of inherited properties:
type
TBase = class
:
protected
property Size: Integer read FSize;
property Text: string read GetText write SetText;
property Color: TColor read FColor write SetColor stored False;
:
end;
type
TDerived = class(TBase)
:
protected
property Size write SetSize;
published
property Text;
property Color stored True default clBlue;
Class-reference types
Class-reference types allow operations to be performed directly on classes. This
contrasts with class types, which allow operations to be performed on instances of
classes. Class-reference types are sometimes referred to as metaclasses or metaclass
types.
class reference type
class of object type identifier
Class methods
A class method is a method that operates on a class reference instead of an object
reference. The definition of a class method must include the reserved word class
before the procedure or function keyword that starts the definition. For example,
type
TFigure = class
public
class function Supports(Operation: string): Boolean; virtual;
class procedure GetInfo(var Info: TFigureInfo); virtual;
:
end;
The defining declaration of a class method must also start with the reserved word
class. For example,
class procedure TFigure.GetInfo(var Info: TFigureInfo);
begin
:
end;
In the defining declaration of a class method, the identifier Self represents the class
for which the method was activated. The type of Self in a class method is class of
ClassType, where ClassType is the class type for which the method is implemented.
Because Self doesn’t represent an object reference in a class method, it isn’t possible
to use Self to access fields, properties, and normal methods. It is possible, however,
to call constructors and other class methods through Self.
A class method can be invoked through a class reference or an object reference.
When invoked through an object reference, the class of the given object reference is
passed as the Self parameter.
10
Exceptions
An exception is generally an error condition or other event that interrupts normal
flow of execution in an application. When an exception is raised, it causes control to
be transferred from the current point of execution to an exception handler. Object
Pascal's exception handling support provides a structured means of separating
normal program logic from error handling logic, greatly increasing the
maintainability and robustness of applications.
Object Pascal uses objects to represent exceptions. This has several advantages, the
key ones of which are
• Exceptions can be grouped into hierarchies using inheritance
• New exceptions can be introduced without affecting existing code
• An exception object can carry information (such as an error message or an error
code) from the point where it was raised to the point where it is handled
exception instance
at address expression
The argument to a raise statement must be an object. In other words, the raise
keyword must be followed by an expression of a class type. When an exception is
raised, the exception handling logic takes ownership of the exception object. Once
the exception is handled, the exception object is automatically destroyed through a
call to the object's Destroy destructor. An application should never attempt to
manually destroy a raised exception object.
try statement
try statement list except exception block end
statement list
statement
exception block
exception handler
statement list
exception handler
on class type identifier do statement
identifier :
A try...except statement executes the statements in the try statement list in sequential
order. If the statements execute without any exceptions being raised, the exception
Re-raising exceptions
In some situations, a procedure or function may need to perform clean-up
operations when an exception occurs, but the procedure or function may not be
prepared to actually handle the exception. For example, consider the GetFileList
function below, which allocates a TStringList object and fills it with the filenames
matching a given search path.
function GetFileList(const Path: string): TStringList;
var
I: Integer;
SearchRec: TSearchRec;
Nested exceptions
Code executed in an exception handler can itself raise and handle exceptions. As
long as exceptions raised in an exception handler are also handled within the
exception handler, they do not affect the original exception. However, once an
exception raised in an exception handler propagates beyond that handler, the
original exception is lost. This is illustrated by the Tan function shown below.
type
ETrigError = class(EMathError);
statement list
statement
A try...finally statement executes the statements in the try statement list in sequential
order. If no exceptions are raised in the try statement list, the finally statement list is
executed. If an exception is raised in the try statement list, control is transferred to
the finally statement list, and once the finally statement list completes execution, the
exception is re-raised. The resulting effect is that the finally statement list is always
executed, regardless of how the try statement list terminates.
The section of code shown below illustrates how a try...finally statement can be
used to ensure that a file that was opened is always closed.
Reset(F);
try
ProcessFile(F);
finally
CloseFile(F);
end;
Note In a typical application, try...finally statements such as the one above tend to be
much more common than try...except statements. Applications written using
Delphi's Visual Class Library, for example, normally rely on VCL's default exception
handling mechanisms, thus seldom needing to use try...except statements. Resource
allocations will however often have to be guarded against exceptions, and the use of
try...finally statements will therefore be much more frequent.
If an exception is raised but not handled in the finally statement list, that exception is
propagated out of the try...finally statement, and any original exception is lost.
Note It is strongly recommended that a finally statement list always handle all local
exceptions, so as to not disturb the propagation of an external exception.
Predefined exceptions
The SysUtils unit declares a number of exception classes, including a class named
Exception which serves as the ultimate ancestor of all exception classes. The public
interface of Exception is declared as follows
type
Exception = class(TObject)
public
constructor Create(const Msg: string);
constructor CreateFmt(const Msg: string; const Args: array of const);
constructor CreateRes(Ident: Word);
constructor CreateResFmt(Ident: Word; const Args: array of const);
constructor CreateHelp(const Msg: string; HelpContext: Longint);
constructor CreateFmtHelp(const Msg: string; const Args: array of const;
HelpContext: Longint);
constructor CreateResHelp(Ident: Word; HelpContext: Longint);
constructor CreateResFmtHelp(Ident: Word; const Args: array of const;
HelpContext: Longint);
destructor Destroy; override;
property HelpContext: Longint;
property Message: string;
end;
The Exception class establishes two properties, Message and HelpContext, that every
exception object inherits. This means that an arbitrary exception object can at least
provide a descriptive message of the exception condition, and possibly a help
context that refers to further on-line help on the topic.
The constructors defined by Exception provide various ways of initializing the
Message and HelpContext properties. In general
• Constructors that don't include "Res" in their names require the exception
message to be specified as a string parameter. Those that do include "Res" in their
names will initialize the Message property from the string resource with the given
ID.
• Constructors that include "Fmt" in their names interpret the specified exception
message as a format string, and require an extra Args parameter which supplies
the format arguments. The initial value of the Message property is constructed
through a call to the Format function in the SysUtils unit.
• Constructors that include "Help" in their names require an extra HelpContext
parameter which supplies the on-line help context for the exception.
Unit syntax
Units are the basis of in Object Pascal. They’re used to create libraries you can
include in various programs without making the source code available, and to
divide large programs into logically related modules.
unit
unit heading ; interface part implementation part
initialization part .
The unit name is used when referring to the unit in a uses clause. The name must be
unique: Two units with the same name can’t be used at the same time.
Note The name of a unit’s source file and binary file must be the same as the unit
identifier, truncated to the first eight characters. If this isn’t the case, the compiler
can’t find the source and/or binary file when compiling a program or unit that uses
the unit.
Unless a procedure or function is inline, the interface part only lists the procedure
or function heading. The block of the procedure or function follows in the
implementation part.
In effect, the procedure and function declarations in the interface part are like
forward declarations, although the forward directive isn’t specified. Therefore,
these procedures and functions can be defined and referenced in any sequence in
the implementation part.
Note Procedure and function headings can be duplicated from the interface part. You
don’t have to specify the formal parameter list, but if you do, the compiler will issue
a compile-time error if the interface and implementation declarations don’t match.
unit Unit2;
interface
uses Unit1;
const b = c;
implementation
end.
unit Unit1;
interface
const c = 1;
implementation
const d = 2;
end.
In the previous example, Unit2 is directly dependent on Unit1 and Prog is directly
dependent on Unit2. Also, Prog is indirectly dependent on Unit1 (through Unit2),
even though none of the identifiers declared in Unit1 are available to Prog.
To compile a module, the compiler must be able to locate all units the module
depends upon, directly or indirectly. So, to compile Prog, the compiler must be able
to locate both Unit1 and Unit2, or else an error occurs.
Note for C and other language users: The uses clauses of an Object Pascal program
provide the “make” logic information traditionally found in make or project files of
other languages. With the uses clause, Object Pascal can build all the dependency
information into the module itself and reduce the chance for error.
When changes are made in the interface part of a unit, other units using the unit
must be recompiled. If you use Make or Build, the compiler does this for you
automatically. If changes are made only to the implementation or the initialization
part, however, other units that use the unit don’t have to be recompiled. In the
previous example, if the interface part of Unit1 is changed (for example, c = 2) Unit2
must be recompiled; changing the implementation part (for example, d = 1) doesn’t
require recompilation of Unit2.
Object Pascal can tell when the interface part of a unit has changed by computing a
unit version number when the unit is compiled. In the preceding example, when Unit2
is compiled, the current version number of Unit1 is saved in the compiled version of
Dynamic-link libraries
12
Dynamic-link libraries (DLLs) permit several Windows applications to share code
and resources. With Object Pascal, you can use DLLs as well as write your own
DLLs to be used by other applications.
What is a DLL?
A DLL is an executable module containing code or resources for use by other
applications or DLLs. Conceptually, a DLL is similar to a unit—both have the ability
to provide services in the form of procedures and functions to a program. There are,
however, many differences between DLLs and units. In particular, units are
statically linked, whereas DLLs are dynamically linked.
When a program uses a procedure or function from a unit, a copy of that procedure
or function’s code is statically linked into the program’s executable file. If two
programs are running simultaneously and they use the same procedure or function
from a unit, there will be two copies of that routine present in the system. It would
be more efficient if the two programs could share a single copy of the routine. DLLs
provide that ability.
In contrast to a unit, the code in a DLL isn’t linked into a program that uses the
DLL. Instead, a DLL’s code and resources are in a separate executable file with a
.DLL extension. This file must be present when the client program runs. The
procedure and function calls in the program are dynamically linked to their entry
points in the DLLs used by the application.
Another difference between units and DLLs is that units can export types, constants,
data, and objects whereas DLLs can export procedures and functions only.
Note A DLL doesn’t have to be written in Object Pascal for a Object Pascal application to
be able to use it. Also, programs written in other languages can use DLLs written in
Object Pascal. DLLs are therefore ideal for multi-language programming projects.
Import units
Declarations of imported procedures and functions can be placed directly in the
program that imports them. Usually, though, they are grouped together in an import
unit that contains declarations for all procedures and functions in a DLL, along with
any constants and types required to interface with the DLL. The WinTypes and
WinProcs units supplied with Delphi are examples of such import units. Import
units aren’t a requirement of the DLL interface, but they do simplify maintenance of
projects that use multiple DLLs.
As an example, consider a DLL called DATETIME.DLL that has four routines to get
and set the date and time, using a record type that contains the day, month, and
year, and another record type that contains the second, minute, and hour. Instead of
specifying the associated procedure, function, and type declarations in every
program that uses the DLL, you can construct an import unit to go along with the
DLL. This code creates a .DCU file, but it doesn’t contribute code or data to the
programs that use it:
unit DateTime;
interface
type
TTimeRec = record
Second: Integer;
Minute: Integer;
Hour: Integer;
end;
type
TDateRec = record
Day: Integer;
Month: Integer;
Year: Integer;
end;
implementation
end.
Any program that uses DATETIME.DLL can now simply specify DateTime in its
uses clause. Here is a Windows program example:
program ShowTime;
var
Time: TTimeRec;
begin
GetTime(Time);
with Time do
WriteLn('The time is ', Hour, ':', Minute, ':', Second);
end.
Another advantage of an import unit such as DateTime is that when the associated
DATETIME.DLL is modified, only one unit, the DateTime import unit, needs
updating to reflect the changes.
When you compile a program that uses a DLL, the compiler doesn’t look for the
DLL so it need not be present. The DLL must be present when you run the program,
however.
Note If you write your own DLLs, they aren’t automatically compiled when you compile
a program that uses the DLL. Instead, DLLs must be compiled separately.
type
TTimeRec = record
Second: Integer;
var
Time: TTimeRec;
Handle: THandle;
GetTime: TGetTime;
begin
Handle := LoadLibrary('DATETIME.DLL');
if Handle >= 32 then
begin
@GetTime := GetProcAddress(Handle, 'GETTIME');
if @GetTime <> nil then
begin
GetTime(Time);
with Time do
WriteLn('The time is ', Hour, ':', Minute, ':', Second);
end;
FreeLibrary(Handle);
end;
end;
Writing DLLs
The structure of a Object Pascal DLL is identical to that of a program, except a DLL
starts with a library header instead of a program header. The library header tells
Object Pascal to produce an executable file with the extension .DLL instead of .EXE,
and also ensures that the executable file is marked as being a DLL.
library
library heading ; block .
uses clause
The example here implements a very simple DLL with two exported functions, Min
and Max, that calculate the smaller and larger of two integer values.
library MinMax;
exports
Min index 1,
Max index 2;
begin
end.
Note the use of the export procedure directive to prepare Min and Max for
exporting, and the exports clause to actually export the two routines, supplying an
optional ordinal number for each of them.
Although the preceding example doesn’t demonstrate it, libraries can and often do
consist of several units. In such cases, the library source file itself is frequently
reduced to a uses clause, an exports clause, and the library’s initialization code. For
example,
library Editors;
exports
InitEditors index 1,
DoneEditors index 2,
InsertText index 3,
DeleteSelection index 4,
FormatSelection index 5,
PrintSelection index 6,
ƒ
SetErrorHandler index 53;
begin
InitLibrary;
end.
exports entry
identifier
index integer constant
An exports clause can appear anywhere and any number of times in a program or
library’s declaration part. Each entry in an exports clause specifies the identifier of a
procedure or function to be exported. That procedure or function must be declared
before the exports clause appears, however, and its declaration must contain the
export directive. You can precede the identifier in the exports clause with a unit
identifier and a period; this is known as a fully qualified identifier.
An exports entry can also include an index clause, which consists of the word index
followed by an integer constant between 1 and 32,767. When an index clause is
specified, the procedure or function to be exported uses the specified ordinal
number. If no index clause is present in an exports entry, an ordinal number is
automatically assigned. The quickest way to look up a DLL entry is by index.
An entry can also have a name clause, which consists of the word name followed by
a string constant. When there is a name clause, the procedure or function to be
exported uses the name specified by the string constant. If no name clause is present
in an exports entry, the procedure or function is exported by its identifier and
converted to all uppercase.
Finally, an exports entry can include the resident keyword. When resident is
specified, the export information stays in memory while the DLL is loaded. The
resident option significantly reduces the time it takes to look up a DLL entry by
name, so if client programs that use the DLL are likely to import certain entries by
name, those entries should be exported using the resident keyword.
A program can contain an exports clause, but it seldom does because Windows
doesn’t allow application modules to export functions for use by other applications.
var
SaveExit: Pointer;
begin
ƒ
{ Perform DLL initialization }
ƒ
SaveExit := ExitProc; { Save old exit procedure pointer }
ExitProc := @LibExit; { Install LibExit exit procedure }
end.
Special characters
When writing to Output or a file that has been assigned to the CRT window, the
following control characters have special meanings:
Table 13-2 Special characters in the WinCrt window
Char Name Description
#7 BELL Emits a beep from the internal speaker.
#8 BS Moves the cursor left one column and erases the character at that position. If the
cursor is already at the left edge of the screen, nothing happens.
#10 LF Moves the cursor down one line. If the cursor is already at the bottom of the virtual
screen, the screen is scrolled up one line.
#13 CR Returns the cursor to the left edge of the screen.
14
Using the 80x87
There are two kinds of numbers you can work with in Object Pascal: integers
(Shortint, Smallint, Longint, Byte, Word, Integer, Cardinal) and reals (Real, Single,
Double, Extended, Comp). Reals are also known as floating-point numbers. The 80x86
family of processors is designed to handle integer values easily, but handling reals is
considerably more difficult. To improve floating-point performance, the 80x86
family of processors has a corresponding family of math coprocessors, the 80x87s.
The 80x87 is a special hardware numeric processor that can be installed in your PC.
It executes floating-point instructions very quickly, so if you use floating point often,
you'll probably want a numeric coprocessor or a 486DX or Pentium processor,
which has a numeric coprocessor built in.
By default, Delphi produces code that uses the 80x87 numeric coprocessor. This
gives you access to all five real types (Real, Single, Double, Extended, and Comp), and
performs all floating-point operations using the full Extended range of 3.4 × 10-4951 to
1.1 × 104932 with 19 to 20 significant digits.
For compatibility with earlier versions of Object Pascal, Delphi provides a $N
compiler switch which allows you to control floating-point code generation. The
default state is {$N+}, and in this state Delphi produces code that uses the 80x87
numeric coprocessor. In the {$N–} state, Delphi supports only the Real type, and
uses a library of software routines to handle floating-point operations. The Real type
provides a range of 2.9 × 10-39 to 1.7 × 1038 with 11 to 12 significant digits.
Note The Delphi Visual Class Library requires that you compile your applications in the
{$N+} state. Unless you are compiling an application that doesn't use VCL, you
should refrain from using the {$N–} state.
To interface with the 80x87 coprocessor, Delphi applications use the WIN87EM.DLL
support library that comes with Windows. If an 80x87 coprocessor isn't present in
your system, WIN87EM.DLL will emulate it in software. Emulation is substantially
slower than the real 80x87 coprocessor, but it does guarantee that an application
using the 80x87 can be run on any machine.
Comparing reals
Because real-type values are approximations, the results of comparing values of
different real types aren't always as expected. For example, if X is a variable of type
Single and Y is a variable of type Double, then these statements are False:
X := 1 / 3;
Y := 1 / 3;
Writeln(X = Y);
This is because X is accurate only to 7 to 8 digits, where Y is accurate to 15 to 16
digits, and when both are converted to Extended, they will differ after 7 to 8 digits.
Similarly, these statements,
X := 1 / 3;
Writeln(X = 1 / 3);
Exception statements
The exceptions statements are the raise statement, the try...except statement, and
the try...finally statement. These statements are described in Chapter 10,
Exceptions.
16
Memory issues
This chapter is about Object Pascal and memory. This chapter explains how
Windows programs use memory, and it also describes the internal data formats
used in Object Pascal.
Code segments
Each module (the main program or library and each unit) in a Delphi application or
DLL has its own code segment. The size of a single code segment can't exceed 64K,
but the total size of the code is limited only by the available memory.
Segment attributes
Each code segment has a set of attributes that determine the behavior of the code
segment when it's loaded into memory.
MOVEABLE or FIXED
When a code segment is MOVEABLE, Windows can move the segment around in
physical memory to satisfy other memory allocation requests. When a code segment
is FIXED, it never moves in physical memory. The preferred attribute is
MOVEABLE, and unless it's absolutely necessary to keep a code segment at the
same address in physical memory (such as if it contains an interrupt handler), you
should use the MOVEABLE attribute. When you do need a fixed code segment,
keep that code segment as small as possible.
DISCARDABLE or PERMANENT
When a segment is DISCARDABLE, Windows can free the memory occupied by the
segment when it needs to allocate additional memory. When a segment is
PERMANENT, it's kept in memory at all times.
When an application makes a call to a DISCARDABLE segment that's not in
memory, Windows first loads it from the .EXE file. This takes longer than if the
segment were PERMANENT, but it allows an application to execute in less space.
Changing attributes
The default attributes of a code segment are MOVEABLE, DEMANDLOAD, and
DISCARDABLE, but you can change this with a $C compiler directive. For example,
{$C MOVEABLE PRELOAD PERMANENT}
For details about the $C compiler directive, see Appendix B.
There is no need for a separate overlay manager. The Windows memory manager
includes a full set of overlay management services, controlled through code segment
attributes. These services are available to any Windows application.
Local heap
Stack
Static data
Task header
The first 16 bytes of the automatic data segment always contain the task header in
which Windows stores various system information.
Integer types
The format selected to represent an integer-type variable depends on its minimum
and maximum bounds:
• If both bounds are within the range -128..127 (Shortint), the variable is stored as
a signed byte.
• If both bounds are within the range 0..255 (Byte), the variable is stored as an
unsigned byte.
• If both bounds are within the range -32768..32767 (Smallint), the variable is
stored as a signed word.
• If both bounds are within the range 0..65535 (Word), the variable is stored as an
unsigned word.
• Otherwise, the variable is stored as a signed double word (Longint).
Char types
A Char or a subrange of a Char type is stored as an unsigned byte.
Enumerated types
An enumerated type is stored as an unsigned byte if the enumeration has no more
than 256 values, and if the type was declared in the {$Z-} state (the default). If an
enumerated type has more than 256 values, or if the type was declared in the {$Z+}
state, it is stored as an unsigned word.
Floating-point types
The floating-point types (Real, Single, Double, Extended, and Comp) store the binary
representations of a sign (+or -), an exponent, and a significand. A represented
number has the value
+/- significand * 2exponent
where the significand has a single bit to the left of the binary decimal point (that is, 0
<= significand < 2).
In the figures that follow, msb means most significant bit and lsb means least
significant bit. The leftmost items are stored at the highest addresses. For example,
for a real-type value, e is stored in the first byte, f in the following five bytes, and s in
the most significant bit of the last byte.
s e f
msb lsb msb lsb
The value v of the number is determined by the following:
if 0 < e < 2047, then v = (-1) s * 2(e-1023) * (1.f).
if e = 0 and f <> 0, then v = (-1) s * 2(-1022) * (0.f).
if e = 0 and f = 0, then v = (-1) s * 0.
if e = 2047 and f = 0, then v = (-1) s * Inf.
if e = 2047 and f <> 0, then v is a NaN.
Pointer types
A Pointer type is stored as two words (a double word), with the offset part in the
low word and the segment part in the high word. The pointer value nil is stored as
a double-word zero.
String types
A string occupies as many bytes as its maximum length plus one. The first byte
contains the current dynamic length of the string, and the following bytes contain
the characters of the string.
The length byte and the characters are considered unsigned values. Maximum string
length is 255 characters plus a length byte (string [255]).
Set types
A set is a bit array where each bit indicates whether an element is in the set or not.
The maximum number of elements in a set is 256, so a set never occupies more than
32 bytes. The number of bytes occupied by a particular set is calculated as
ByteSize = (Max div 8) - (Min div 8) + 1
where Min and Max are the lower and upper bounds of the base type of that set.
The byte number of a specific element E is
ByteNumber = (E div 8) - (Min div 8)
and the bit number within that byte is
BitNumber = E mod 8
where E denotes the ordinal value of the element.
Record types
The fields of a record are stored as a contiguous sequence of variables. The first field
is stored at the lowest memory address. If the record contains variant parts, then
each variant starts at the same memory address.
File types
File types are represented as records. Typed files and untyped files occupy 128
bytes, which are laid out as follows:
type
TFileRec = record
Handle: Word;
Mode: Word;
RecSize: Word;
Private: array [1..26] of Byte;
UserData: array [1..16] of Byte;
Name: array [0..79] of Char;
end;
Text files occupy 256 bytes, which are laid out as follows:
type
TTextBuf = array [0..127] of Char;
TTextRec = record
Handle: Word;
Mode: Word;
BufSize: Word;
Private: Word;
BufPos: Word;
BufEnd: Word;
BufPtr: ^TTextBuf;
OpenFunc: Pointer;
InOutFunc: Pointer;
FlushFunc: Pointer;
CloseFunc: Pointer;
UserData: array [1..16] of Byte;
Name: array [0..79] of Char;
Buffer: TTextBuf;
end;
Handle contains the file's handle (when the file is open).
For text files, BufPtr is a pointer to a buffer of BufSize bytes, BufPos is the index of
the next character in the buffer to read or write, and BufEnd is a count of valid
characters in the buffer. OpenFunc, InOutFunc, FlushFunc, and CloseFunc are pointers
to the I/O routines that control the file. The section entitled "Text file device drivers"
in Chapter 13 provides information on that subject.
Procedural types
A global procedure pointer type is stored as a 32-bit pointer to the entry point of a
procedure or function.
A method pointer type is stored as a 32-bit pointer to the entry point of a method,
followed by a 32-bit pointer to an object.
Class types
A class type value is stored as a 32-bit pointer to an instance of the class. An
instance of a class is also known as an object.
The internal data format of an object resembles that of a record. The fields of an
object are stored in order of declaration as a contiguous sequences of variables. Any
fields inherited from an ancestor class are stored before the new fields defined in the
descendant class.
The first four-byte field of every object is a pointer to the virtual method table (VMT)
of the class. There is only one VMT per class (not one per instance), but two distinct
class types never share a VMT, no matter how identical they appear to be. VMTs are
built automatically by the compiler, and are never directly manipulated by a
17
Control issues
This chapter describes in detail the various ways that Delphi implements program
control. Included are calling conventions and exit procedures.
Calling conventions
Parameters are transferred to procedures and functions via the stack. Before calling
a procedure or function, the parameters are pushed onto the stack in their order of
declaration. Before returning, the procedure or function removes all parameters
from the stack.
The skeleton code for a procedure or function call looks like this:
PUSH Param1
PUSH Param2
.
.
.
PUSH ParamX
CALL ProcOrFunc
Parameters are passed either by reference or by value. When a parameter is passed by
reference, a pointer that points to the actual storage location is pushed onto the
stack. When a parameter is passed by value, the actual value is pushed onto the
stack.
Variable parameters
Variable parameters (var parameters) are always passed by referencea pointer
that points to the actual storage location.
Open parameters
Open string parameters are passed by first pushing a pointer to the string and then
pushing a word containing the size attribute (maximum length) of the string.
Open array parameters are passed by first pushing a pointer to the array and then
pushing a word containing the number of elements in the array less one.
Function results
Ordinal-type function results are returned in the CPU registers: Bytes are returned
in AL, words are returned in AX, and double words are returned in DX:AX (high-
order word in DX, low-order word in AX).
Real-type function results (type Real) are returned in the DX:BX:AX registers (high-
order word in DX, middle word in BX, low-order word in AX).
80x87-type function results (type Single, Double, Extended, and Comp) are returned in
the 80x87 coprocessor's top-of-stack register (ST(0)).
Pointer-type, class-type, and class-reference-type function results are returned in
DX:AX (segment part in DX, offset part in AX).
For a string-type function result, the caller pushes a pointer to a temporary storage
location before pushing any parameters, and the function returns a string value in
that temporary location. The function must not remove the pointer.
For array, record, and set type function results, if the value occupies one byte, it is
returned in AL, if the value occupies two bytes, it is returned in AX, and if the value
occupies four bytes, it is returned in DX:AX. Otherwise, the caller pushes a pointer
to a temporary storage location of the appropriate size, and the function returns the
result in that temporary location. Upon returning, the function leaves the temporary
pointer on the stack.
A global procedure pointer type is returned in DX:AX.
A method pointer type is returned in BX:CX:DX:AX, where DX:AX contains the
method pointer and BX:CX contains the instance pointer.
Register-saving conventions
Procedures and functions should preserve the BP, SP, SS, and DS registers. All other
registers can be modified. In addition, exported routines should preserve the SI and
DI registers.
Exit procedures
By installing an exit procedure, you can gain control over a program's termination
process. This is useful when you want to make sure specific actions are carried out
before a program terminates; a typical example is updating and closing files.
The ExitProc pointer variable allows you to install an exit procedure. The exit
procedure is always called as a part of a program's termination, whether it's a
normal termination, a termination through a call to Halt, or a termination due to a
run-time error.
An exit procedure takes no parameters and must be compiled with a far procedure
directive to force it to use the far call model.
When implemented properly, an exit procedure actually becomes part of a chain of
exit procedures. This chain makes it possible for units as well as programs to install
exit procedures. Some units install an exit procedure as part of their initialization
code and then rely on that specific procedure to be called to clean up after the unit.
Constant folding
If the operand(s) of an operator are constants, Delphi evaluates the expression at
compile time. For example,
X := 3 + 4 * 2
generates the same code as X := 11 and
S := 'In' + 'Out'
generates the same code as S := 'InOut'.
Likewise, if an operand of an Abs, Chr, Hi, Length, Lo, Odd, Ord, Pred, Ptr, Round,
Succ, Swap, or Trunc function call is a constant, the function is evaluated at compile
time.
If an array index expression is a constant, the address of the component is evaluated
at compile time. For example, accessing Data [5, 5] is just as efficient as accessing a
simple variable.
Constant merging
Using the same string constant two or more times in a statement part generates only
one copy of the constant. For example, two or more Write('Done') statements in the
same statement part references the same copy of the string constant 'Done'.
Constant parameters
Whenever possible, you should use constant parameters instead of value
parameters. Constant parameters are at least as efficient as value parameters and, in
many cases, more efficient. In particular, constant parameters generate less code and
execute faster than value parameters for structured and string types.
Constant parameters are more efficient than value parameters because the compiler
doesn't have to generate copies of the actual parameters upon entry to procedures
or functions. Value parameters have to be copied into local variables so that
modifications made to the formal parameters won't modify the actual parameters.
Because constant formal parameters can't be modified, the compiler has no need to
generate copies of the actual parameters and code and stack space is saved. Read
more about constant parameters on page 76.
Small sets
The compiler generates very efficient code for operations on small sets. A small set is
a set with a lower bound ordinal value in the range 0..7 and an upper bound ordinal
value in the range 0..15. For example, the following TByteSet and TWordSet are both
small sets.
type
TByteSet = set of 0..7;
TWordSet = set of 0..15;
Small set operations, such as union (+), difference (-), intersection (*), and inclusion
tests (in) are generated inline using AND, OR, NOT, and TEST machine code
instructions instead of calls to run-time library routines. Likewise, the Include and
Exclude standard procedures generate inline code when applied to small sets.
Order of evaluation
As permitted by the Pascal standards, operands of an expression are frequently
evaluated differently from the left to right order in which they are written. For
example, the statement
I := F(J) div G(J);
Range checking
Assignment of a constant to a variable and use of a constant as a value parameter is
range-checked at compile time; no run-time range-check code is generated. For
example, X := 999, where X is of type Byte, causes a compile-time error.
Smart linking
Delphi’s built-in linker automatically removes unused code and data when building
an .EXE file. Procedures, functions, variables, and typed constants that are part of
the compilation, but are never referenced, are removed from the .EXE file. The
removal of unused code takes place on a per procedure basis; the removal of
unused data takes place on a per declaration section basis.
Consider the following program:
program SmartLink;
const
H: array [0..15] of Char = '0123456789ABCDEF';
var
I, J: Integer;
X, Y: Real;
var
S: string [79];
var
A: array [1..10000] of Integer;
procedure P1;
begin
A[1] := 1;
end;
procedure P2;
begin
I := 1;
end;
procedure P3;
begin
S := 'Borland Pascal';
P2;
end;
begin
P3;
end.
The main program calls P3, which calls P2, so both P2 and P3 are included in the
.EXE file. Because P2 references the first var declaration section, and P3 references
the second var declaration, I, J, X, Y, and S are also included in the .EXE file. No
references are made to P1, however, and none of the included procedures reference
H and A, so these objects are removed.
An example of such a unit is the SysUtils standard unit: It contains a number of
procedures and functions, all of which are seldom used by the same program. If a
Register use
In general, the rules of register use in an asm statement are the same as those of an
external procedure or function. An asm statement must preserve the BP, SP, SS, and
DS registers, but can freely modify the AX, BX, CX, DX, SI, DI, ES, and Flags
registers. On entry to an asm statement, BP points to the current stack frame, SP
points to the top of the stack, SS contains the segment address of the stack segment,
and DS contains the segment address of the data segment. Except for BP, SP, SS,
and DS, an asm statement can assume nothing about register contents on entry to
the statement.
Labels
Labels are defined in assembler as they are in Object Pascal--by writing a label
identifier and a colon before a statement. There is no limit to a label length, except
only the first 32 characters of an identifier are significant in the built-in assembler.
And as they are in Object Pascal, labels defined in assembler must be declared in a
label declaration part in the block containing the asm statement. There is one
exception to this rule: local labels.
Instruction opcodes
The built-in assembler supports all 8086/8087 and 80286/80287 instruction opcodes.
8087 opcodes are available only in the {$N+} state (numeric processor enabled),
80286 opcodes are available only in the {$G+} state (80286 code generation enabled),
and 80287 opcodes are available only in the {$G+,N+} state.
For a complete description of each instruction, refer to your 80x86 and 80x87
reference manuals.
Assembler directives
Delphi's built-in assembler supports three assembler directives: DB (define byte),
DW (define word), and DD (define double word). They each generate data
corresponding to the comma-separated operands that follow the directive.
The DB directive generates a sequence of bytes. Each operand can be a constant
expression with a value between -128 and 255, or a character string of any length.
Constant expressions generate one byte of code, and strings generate a sequence of
bytes with values corresponding to the ASCII code of each character.
The DW directive generates a sequence of words. Each operand can be a constant
expression with a value between -32,768 and 65,535, or an address expression. For
an address expression, the built-in assembler generates a near pointer, that is, a
word that contains the offset part of the address.
The DD directive generates a sequence of double words. Each operand can be a
constant expression with a value between -2,147,483,648 and 4,294,967,295, or an
address expression. For an address expression, the built-in assembler generates a far
pointer, that is, a word that contains the offset part of the address, followed by a
word that contains the segment part of the address.
Operands
Built-in assembler operands are expressions that consist of a combination of
constants, registers, symbols, and operators. Although built-in assembler
expressions are built using the same basic principles as Object Pascal expressions,
there are a number of important differences, as will be explained later in this
chapter.
Within operands, the following reserved words have a predefined meaning to the
built-in assembler:
Table 19-1 Built-in assembler reserved words
AH CS LOW SI
AL CX MOD SP
AND DH NEAR SS
AX DI NOT ST
BH DL OFFSET TBYTE
BL DS OR TYPE
BP DWORD PTR WORD
BX DX QWORD XOR
BYTE ES SEG
CH FAR SHL
CL HIGH SHR
The reserved words always take precedence over user-defined identifiers. For
example, the code fragment,
var
ch: Char;
.
.
.
asm
MOV CH, 1
end;
loads 1 into the CH register, not into the CH variable. To access a user-defined
symbol with the same name as a reserved word, you must use the ampersand (&)
identifier override operator:
asm
MOV &ch, 1
end;
It's strongly suggested that you avoid user-defined identifiers with the same names
as built-in assembler reserved words, because such name confusion can easily lead
to obscure and hard-to-find bugs.
Expression elements
The basic elements of an expression are constants, registers, and symbols.
Constants
The built-in assembler supports two types of constants: numeric constants and string
constants.
Numeric constants
Numeric constants must be integers, and their values must be between -
2,147,483,648 and 4,294,967,295.
By default, numeric constants use decimal (base 10) notation, but the built-in
assembler supports binary (base 2), octal (base 8), and hexadecimal (base 16)
notations as well. Binary notation is selected by writing a B after the number, octal
notation is selected by writing a letter O after the number, and hexadecimal notation
is selected by writing an H after the number or a $ before the number.
The B, O, and H suffixes aren't supported in Object Pascal expressions. Object Pascal
expressions allow only decimal notation (the default) and hexadecimal notation
(using a $ prefix).
Numeric constants must start with one of the digits 0 through 9 or a $ character;
therefore when you write a hexadecimal constant using the H suffix, an extra zero in
front of the number is required if the first significant digit is one of the hexadecimal
digits A through F. For example, 0BAD4H and $BAD4 are hexadecimal constants,
but BAD4H is an identifier because it starts with a letter and not a digit.
String constants
String constants must be enclosed in single or double quotes. Two consecutive
quotes of the same type as the enclosing quotes count as only one character. Here
are some examples of string constants:
'Z'
'Delphi'
"That's all folks"
'"That''s all folks," he said.'
Registers
The following reserved symbols denote CPU registers:
Table 19-3 CPU registers
16-bit general purpose AX BX CX DX
8-bit low registers AL BL CL DL
8-bit high registers AH BH CH DH
16-bit pointer or index SP BP SI DI
16-bit segment registers CS DS SS ES
8087 register stack ST
When an operand consists solely of a register name, it's called a register operand.
All registers can be used as register operands. In addition, some registers can be
used in other contexts.
The base registers (BX and BP) and the index registers (SI and DI) can be written
within square brackets to indicate indexing. Valid base/index register combinations
are [BX], [BP], [SI], [DI], [BX+SI], [BX+DI], [BP+SI], and [BP+DI].
Symbols
The built-in assembler allows you to access almost all Object Pascal symbols in
assembler expressions, including labels, constants, types, variables, procedures, and
functions. In addition, the built-in assembler implements the following special
symbols:
@Code @Data @Result
The @Code and @Data symbols represent the current code and data segments. They
should only be used in conjunction with the SEG operator:
asm
MOV AX,SEG @Data
MOV DS,AX
end;
The @Result symbol represents the function result variable within the statement part
of a function. For example, in this function,
function Sum(X, Y: Integer): Integer;
begin
Sum := X + Y;
end;
the statement that assigns a function result value to Sum uses the @Result variable if
it is written in built-in assembler:
function Sum(X, Y: Integer): Integer;
begin
asm
MOV AX,X
ADD AX,Y
MOV @Result,AX
end;
end;
The following symbols can't be used in built-in assembler expressions:
• Standard procedures and functions (for example, WriteLn, Chr)
• The Mem, MemW, MemL, Port, and PortW special arrays
• String, floating-point, and set constants
• Procedures and functions declared with the inline directive
• Labels that aren't declared in the current block
Expression classes
The built-in assembler divides expressions into three classes: registers, memory
references, and immediate values.
An expression that consists solely of a register name is a register expression.
Examples of register expressions are AX, CL, DI, and ES. Used as operands, register
expressions direct the assembler to generate instructions that operate on the CPU
registers.
Expression types
Every built-in assembler expression has an associated type--or more correctly, an
associated size, because the built-in assembler regards the type of an expression
simply as the size of its memory location. For example, the type (size) of an Integer
variable is two, because it occupies 2 bytes.
The built-in assembler performs type checking whenever possible, so in the
instructions
var
QuitFlag: Boolean;
OutBufPtr: Word;
.
.
.
asm
MOV AL,QuitFlag
MOV BX,OutBufPtr
end;
the built-in assembler checks that the size of QuitFlag is one (a byte), and that the
size of OutBufPtr is two (a word). An error results if the type check fails; for
example, this isn't allowed:
asm
MOV DL,OutBufPtr
end;
The problem is DL is a byte-sized register and OutBufPtr is a word. The type of a
memory reference can be changed through a typecast; these are correct ways of
writing the previous instruction:
asm
MOV DL,BYTE PTR OutBufPtr
MOV DL,Byte(OutBufPtr)
MOV DL,OutBufPtr.Byte
end;
These MOV instructions all refer to the first (least significant) byte of the OutBufPtr
variable.
In some cases, a memory reference is untyped; that is, it has no associated type. One
example is an immediate value enclosed in square brackets:
asm
MOV AL,[100H]
MOV BX,[100H]
end;
Notice in particular the NEAR and FAR pseudotypes, which are used by procedure
and function symbols to indicate their call model. You can use NEAR and FAR in
typecasts just like other symbols. For example, if FarProc is a FAR procedure,
procedure FarProc; far;
and if you are writing built-in assembler code in the same module as FarProc, you
can use the more efficient NEAR call instruction to call it:
asm
PUSH CS
CALL NEAR PTR FarProc
end;
Expression operators
The built-in assembler provides a variety of operators, divided into 12 classes of
precedence. The following table lists the built-in assembler's expression operators in
decreasing order of precedence.
Built-in assembler operator precedence is different from Object Pascal. For example,
in a built-in assembler expression, the AND operator has lower precedence than the
plus (+) and minus (-) operators, whereas in a Object Pascal expression, it has higher
precedence.
Table 19-6 Summary of built-in asssembler expression operators
Operator(s) Comments
& Identifier override operator
@2:
STOSB
LOOP @1
@3:
ASSUME CS:CODE
PUBLIC UpperCase, StringOf ;Make them known
PUSH BP ;Save BP
MOV BP, SP ;Set up stack frame
PUSH DS ;Save DS
LDS SI, Upperstr ;Load string address
LES DI, Upperres ;Load result address
CLD ;Forward string-ops
LODSB ;Load string length
STOSB ;Copy to result
MOV CL, AL ;String length to CX
XOR CH, CH
JCXZ U3 ;Skip if empty string
U1: LODSB ;Load character
CMP AL, 'a' ;Skip if not 'a'..'z'
JB U2
CMP AL, 'z'
JA U2
SUB AL, 'a'-'A' ;Convert to uppercase
U2: STOSB ;Store in result
LOOP U1 ;Loop for all characters
U3: POP DS ;Restore DS
POP BP ;Restore BP
RET 4 ;Remove parameter and return
UpperCase ENDP
PUSH BP ;Save BP
MOV BP, SP ;Set up stack frame
LES DI, StrOfRes ;Load result address
MOV AL, StrOfCount ;Load count
CLD ;Forward string-ops
STOSB ;Store length
MOV CL, AL ;Count to CX
XOR CH, CH
MOV AL, StrOfChar ;Load character
REP STOSB ;Store string of characters
POP BP ;Restore BP
RET 8 ;Remove parameters and return
StringOf ENDP
CODE ENDS
Inline statements
An inline statement consists of the reserved word inline followed by one or more
inline elements, separated by slashes and enclosed in parentheses:
inline (10/$2345/Count + 1/Data - Offset);
Here's the syntax of an inline statement:
Each inline element consists of an optional size specifier, < or >, and a constant or a
variable identifier, followed by zero or more offset specifiers (see the syntax that
follows). An offset specifier consists of a + or a - followed by a constant.
Inline directives
With inline directives, you can write procedures and functions that expand into a
given sequence of machine code instructions whenever they are called. These are
comparable to macros in assembly language. The syntax for an inline directive is
the same as that of an inline statement:
When a normal procedure or function is called (including one that contains inline
statements), the compiler generates code that pushes the parameters (if any) onto
the stack, and then generates a CALL instruction to call the procedure or function.
However, when you call an inline procedure or function, the compiler generates
code from the inline directive instead of the CALL. Here's a short example of two
inline procedures:
procedure DisableInterrupts; inline ($FA); { CLI }
procedure EnableInterrupts; inline ($FB); { STI }
When DisableInterrupts is called, it generates 1 byte of code--a CLI instruction.
Procedures and functions declared with inline directives can have parameters;
however, the parameters can't be referred to symbolically in the inline directive
(other variables can, though). Also, because such procedures and functions are in
fact macros, there is no automatic entry and exit code, nor should there be any
return instruction.
The following function multiplies two Integer values, producing a Longint result:
function LongMul(X, Y: Integer): Longint;
inline (
$5A/ { POP AX ;Pop X }
$58/ { POP DX ;Pop Y }
$F7/$EA); { IMUL DX ;DX : AX = X * Y }
Note the lack of entry and exit code and the missing return instruction. These aren't
required, because the 4 bytes are inserted into the instruction stream when LongMul
is called.
If you type DCC alone at the command line, a list of command-line compiler options
appears on your screen.
Directory options
The command-line compiler supports several options that allow you to specify the
six directory lists used by the command-line compiler: DSL & CFG, EXE & DCU,
Include, Unit, Resource, and Object.
Excluding the EXE and DCU directory option, you can specify one or multiple
directories for each command-line directory option. If you specify multiple
directories, separate them with semicolons (;). For example, this command line tells
the command-line compiler to search for Include files in C:\DELPHI\INCLUDE
and D:\INC after searching the current directory:
DCC MYSTUFF /IC:\DELPHI\INCLUDE;D:\INC
If you specify multiple directives, the directory lists are concatenated. Therefore,
DCC MYSTUFF /IC:\DELPHI\INCLUDE /ID:\INC
is equivalent to the first example.
Debug options
The command-line compiler has two command-line options that enable you to
generate debugging information: the map file option and the debugging option.
Compiler directives
B
This appendix describes the compiler directives you can use to control the features
of the Delphi compiler. They are listed alphabetically. Each compiler directive is
classified as either a switch, parameter, or conditional compilation directive.
Following the list of compiler directives is a brief discussion of how to use the
conditional compilation directives. This reference section describes how to use
conditional constructs and symbols to produce different code from the same source
text.
A compiler directive is a comment with a special syntax. Delphi allows compiler
directives wherever comments are allowed. A compiler directive starts with a $ as
the first character after the opening comment delimiter, immediately followed by a
name (one or more letters) that designates the particular directive. You can include
comments after the directive and any necessary parameters.
Three types of directives are described in this appendix:
• Switch directives turn particular compiler features on or off by specifying + or -
immediately after the directive name. Switch directives are either global or local.
Global directives affect the entire compilation and must appear before the
|
directive until the next occurrence of the same directive. They can appear
anywhere.
Switch directives can be grouped in a single compiler directive comment by
|
Align data
Type Switch
Syntax {$A+} or {$A-}
Default {$A+}
Scope Global
Remarks
The $A directive switches between byte and word alignment of variables and typed
constants. On all 80x86 CPUs, word alignment means faster execution because
word-sized items on even addresses are accessed in one memory cycle rather than
two memory cycles for words on odd addresses.
In the {$A+} state, all variables and typed constants larger than one byte are aligned
on a machine-word boundary (an even-numbered address). If required, unused
bytes are inserted between variables to achieve word alignment. The {$A+} directive
does not affect byte-sized variables, nor does it affect fields of record structures and
elements of arrays. A field in a record will align on a word boundary only if the
Boolean evaluation
Type Switch
Syntax {$B+} or {$B-}
Default {$B-}
Scope Local
Remarks
The $B directive switches between the two different models of code generation for
the and and or Boolean operators.
In the {$B+} state, the compiler generates code for complete Boolean expression
evaluation. This means that every operand of a Boolean expression built from the
and and or operators is guaranteed to be evaluated, even when the result of the
entire expression is already known.
In the {$B-} state, the compiler generates code for short-circuit Boolean expression
evaluation, which means that evaluation stops as soon as the result of the entire
expression becomes evident.
For further details, see the section "Boolean operators" in Chapter 5, "Expressions."
Remarks
The $C directive is used to control the attributes of a code segment. Every code
segment in an application or library has a set of attributes that determine the
behavior of the code segment when it is loaded into memory. For example, you can
specify that a code segment is moveable, which means Windows can move the code
Debug information
Type Switch
Syntax {$D+} or {$D-}
Default {$D+}
Scope Global
Remarks
The $D directive enables or disables the generation of debug information. This
information consists of a line-number table for each procedure, which maps object-
code addresses into source text line numbers.
For units, the debug information is recorded in the unit file along with the unit's
object code. Debug information increases the size of unit file and takes up additional
room when compiling programs that use the unit, but it does not affect the size or
speed of the executable program.
DEFINE directive
Type Conditional compilation
Syntax {$DEFINE name}
Remarks
Defines a conditional symbol with the given name. The symbol is recognized for the
remainder of the compilation of the current module in which the symbol is declared,
or until it appears in an {$UNDEF name} directive. The {$DEFINE name} directive
has no effect if name is already defined.
Description
Type Parameter
Syntax {$D text}
Scope Global
Remarks
The $D directive inserts the text you specify into the module description entry in the
header of a .EXE or .DLL.Traditionally the text is a name and version number, but
you may specify any text of your choosing. For example,
{$D My Application version 12.5}
ELSE directive
Type Conditional compilation
Syntax {$ELSE}
ENDIF directive
Type Conditional compilation
Syntax {$ENDIF}
Remarks
Ends the conditional compilation initiated by the last {$IFxxx} directive.
Extended syntax
Type Switch
Syntax {$X+} or {$X-}
Default {$X+}
Scope Global
Remarks
The $X directive enables or disables Delphi's extended syntax:
• Function statements. In the {$X+} mode, function calls can be used as procedure
calls; that is, the result of a function call can be discarded. Generally, the
computations performed by a function are represented through its result, so
discarding the result makes little sense. However, in certain cases a function can
carry out multiple operations based on its parameters and some of those cases
might not produce a useful result. When that happens, the {$X+} extensions allow
the function to be treated as a procedure.
• Null-terminated strings. A {$X+} compiler directive enables Delphi's support for
null-terminated strings by activating the special rules that apply to the built-in
PChar type and zero-based character arrays. For more details about null-
terminated strings, see Chapter 15, "Using null-terminated strings."
Note The {$X+} directive does not apply to built-in functions (those defined in the System
unit).
Remarks
The $F directive determines which call model to use for subsequently compiled
procedures and functions. Procedures and functions compiled in the {$F+} state
always use the far call model. In the {$F-} state, Delphi automatically selects the
appropriate model: far if the procedure or function is declared in the interface
section of a unit; otherwise it is near.
The near and far call models are described in full in Chapter 17, "Control issues."
Remarks
The $G directive enables or disables 80286 code generation. In the {$G-} state, only
generic 8086 instructions are generated, and programs compiled in this state can run
on any 80x86 family processor. You can specify {$G-} any place within your code.
In the {$G+} state, the compiler uses the additional instructions of the 80286 to
improve code generation, but programs compiled in this state cannot run on 8088
and 8086 processors. Additional instructions used in the {$G+} state include
ENTER, LEAVE, PUSH immediate, extended IMUL, and extended SHL and SHR.
Remarks
The $G directive lets you specify groups of units you want the linker to place in the
same segment. Grouping units in the same segment ensures that the units swap in
and out of memory at the same time. The $G directive is primarily used to group
units containing discardable code.
Each $G directive specifies a group of units. $G directives are valid only in a
program or library, and must appear after the program or library's uses clause. The
IFDEF directive
Type Conditional compilation
Syntax {$IFDEF name}
Remarks
Compiles the source text that follows it if name is defined.
IFNDEF directive
Type Conditional compilation
Syntax {$IFNDEF name}
Remarks
Compiles the source text that follows it if name is not defined.
IFOPT directive
Type Conditional compilation
Syntax {$IFOPT switch}
Remarks
Compiles the source text that follows it if switch is currently in the specified state.
switch consists of the name of a switch option, followed by a + or a - symbol. For
example, the construct
{$IFOPT N+}
type Real = Extended;
Include file
Type Parameter
Syntax {$I filename}
Scope Local
Remarks
The $I parameter directive instructs the compiler to include the named file in the
compilation. In effect, the file is inserted in the compiled text right after the {$I
filename} directive. The default extension for filename is .PAS. If filename does not
specify a directory path, then, in addition to searching for the file in the same
directory as the current module, Delphi searches in the directories specified in the
Search path input box on the Directories/Conditionals page of the Options|Project
dialog (or in the directories specified in a /I option on the DCC command line).
There is one restriction to the use of include files: An include file can't be specified in
the middle of a statement part. In fact, all statements between the begin and end of
a statement part must exist in the same source file.
Input/output checking
Type Switch
Syntax {$I+} or {$I-}
Default {$I+}
Scope Local
Remarks
The $I switch directive enables or disables the automatic code generation that
checks the result of a call to an I/O procedure. I/O procedures are described in
Chapter 13, "Input and output." If an I/O procedure returns a nonzero I/O result
when this switch is on, an EInOutError exception is raised (or the program is
terminated if exception handling is not enabled). When this switch is off, you must
check for I/O errors by calling IOResult.
Remarks
The $L parameter instructs the compiler to link the named file with the program or
unit being compiled. The $L directive is used to link with code written in for
subprograms declared to be external. The named file must be an Intel relocatable
object file (.OBJ file). The default extension for filename is .OBJ. If filename does not
specify a directory path, then, in addition to searching for the file in the same
directory as the current module, Delphi searches in the directories specified in the
Search path input box on the Directories/Conditionals page of the Options|Project
dialog (or in the directories specified in a /O option on the DCC command line). For
further details about linking with assembly language, see Chapter 20, "Linking
assembler code."
Remarks
The $L switch directive enables or disables the generation of local symbol
information. Local symbol information consists of the names and types of all local
variables and constants in a module, that is, the symbols in the module's
implementation part and the symbols within the module's procedures and
functions.
For units, the local symbol information is recorded in the unit file along with the
unit's object code. Local symbol information increases the size of unit files and takes
up additional room when compiling programs that use the unit, but it does not
affect the size or speed of the executable program.
When a program or unit is compiled in the {$L+} state, Delphi's integrated debugger
lets you examine and modify the module's local variables. Furthermore, calls to the
module's procedures and functions can be examined via the View|Call Stack.
The Include TDW debug info and Map file options on the Linker page of the
Options|Project dialog produce local symbol information for a given module only if
that module was compiled in the {$L+} state.
The $L switch is usually used in conjunction with the $D switch, which enables and
disables the generation of line-number tables for debugging. The $L directive is
ignored if the compiler is in the {$D-} state.
Remarks
The $M directive specifies an application or library's memory allocation parameters.
stacksize must be an integer number in the range 1,024 to 65,520 which specifies the
size of the stack segment. Heapsize specifies the size of the local heap area in the data
segment. heapsize must be an integer number in the range 0 to 65520.
The $M directive has no effect when used in a unit. Furthermore, the stacksize
parameter in a $M directive is ignored in a library (a library always uses the stack of
the applications that call it).
Numeric coprocessor
Type Switch
Syntax {$N+} or {$N-}
Default {$N+}
Scope Global
Remarks
The $N directive switches between the two different models of floating-point code
generation supported by Delphi. In the {$N-} state, code is generated to perform all
real-type calculations in software by calling run-time library routines. In the {$N+}
state, code is generated to perform all real-type calculations using the 80x87 numeric
coprocessor.
Overflow checking
Type Switch
Syntax {$Q+} or {$Q-}
Default {$Q-}
Scope Local
Remarks
The $Q directive controls the generation of overflow checking code. In the {$Q+}
state, certain integer arithmetic operations (+, -, *, Abs, Sqr, Succ, and Pred) are
checked for overflow. The code for each of these integer arithmetic operations is
followed by additional code that verifies that the result is within the supported
range. If an overflow check fails, an EIntOverflow exception is raised (or the program
is terminated if exception handling is not enabled).
The {$Q+} does not affect the Inc and Dec standard procedures. These procedures
are never checked for overflow.
The $Q switch is usually used in conjunction with the $R switch, which enables and
disables the generation of range-checking code. Enabling overflow checking slows
down your program and makes it somewhat larger, so use {$Q+} only for
debugging.
For processors that do not exhibit the FDIV flaw, {$U+} results in only a slight
performance degredation. For a flawed Pentium processor, floating-point divide
operations may take up to three times longer in the {$U+} state, but they will always
produce correct results.
In the {$U-} state, floating-point divide operations are performed using in-line FDIV
instructions. This results in optimum speed and code size, but may produce
incorrect results on flawed Pentium processors. You should use the {$U-} state only
in cases where you are certain that the code is not running on a flawed Pentium
processor.
Range checking
Type Switch
Syntax {$R+} or {$R-}
Default {$R-}
Scope Local
Remarks
The $R directive enables or disables the generation of range-checking code. In the
{$R+} state, all array and string-indexing expressions are verified as being within the
defined bounds, and all assignments to scalar and subrange variables are checked to
be within range. If a range check fails, an ERangeError exception is raised (or the
program is terminated if exception handling is not enabled).
Enabling range checking slows down your program and makes it somewhat larger,
so use the {$R+} only for debugging.
Resource file
Type Parameter
Syntax {$R Filename}
Scope Local
Remarks
The $S parameter directive is valid only in a main program or library. The directive
specifies the preferred size of code segments for grouped units. The specified size
must be in the range 0..65,535. Units that exceed the specified size are placed in their
own code segments.
When grouping units, the linker puts units with the same code segment attributes
into the same code segment, up to the size specified. The limit also applies to groups
specified by the $G directive. Grouping of units is explained under the $G directive.
The $S directive never produces warnings or error messages. If a unit can't fit into a
code segment with other units, it automatically is placed into a separate segment.
Setting the preferred segment size to 0 guarantees that every unit goes in a separate
code segment; this was the default behavior in previous versions of the compiler.
Smart callbacks
Type Switch
Syntax {$K+} or {$K-}
Default {$K+}
Scope Global
Remarks
The $K directive controls the generation of smart callbacks for procedures and
functions that are exported by an application. When an application is compiled in
the {$K-} state, it must use the MakeProcInstance and FreeProcInstance Windows API
routines when it creates callback routines. In the default {$K+} state, the application
itself can call exported entry points, and there is no need to use MakeProcInstance
and FreeProcInstance.
For more details about smart callbacks, see "Entry and exit code" in Chapter 17.
Stack-overflow checking
Type Switch
Syntax {$S+} or {$S-}
Default {$S+}
Scope Local
Remarks
The $Y directive enables or disables generation of symbol reference information.
This information consists of tables that provide the line numbers of all declarations
of and references to symbols in a module.
For units, the symbol reference information is recorded in the .DCU file along with
the unit's object code. Symbol reference information increases the size of the .DCU
files, but it does not affect the size or speed of the executable program.
When a program or unit is compiled in the {$Y+} state, Delphi's integrated browser
can display symbol definition and reference information for that module.
The $Y switch is usually used in conjunction with the $D and $L switches, which
control generation of debug information and local symbol information. The $Y
directive has no effect unless both $D and $L are enabled.
Type-checked pointers
Type Switch
Syntax {$T+} or {$T-}
Default {$T-}
Scope Global
Remarks
The $T directive controls the types of pointer values generated by the @ operator. In
the {$T-} state, the result type of the @ operator is always an untyped pointer that is
compatible with all other pointer types. When @ is applied to a variable reference in
UNDEF directive
Type Conditional compilation
Syntax {$UNDEF name}
Remarks
Undefines a previously defined conditional symbol. The symbol is forgotten for the
remainder of the compilation or until it reappears in a {$DEFINE name} directive.
The {$UNDEF name} directive has no effect if name is already undefined.
Var-string checking
Type Switch
Syntax {$V+} or {$V-}
Default {$V+}
Scope Local
Remarks
The $V directive controls type checking on strings passed as variable parameters. In
the {$V+} state, strict type checking is performed, requiring the formal and actual
parameters to be of identical string types. In the {$V-} (relaxed) state, any string type
variable is allowed as an actual parameter, even if the declared maximum length is
not the same as that of the formal parameter.
The {$V-} state essentially provides an "unsafe" version of open string parameters.
Although {$V-} is still supported, you should use open string parameters. For
additional information, see "Open string parameters" in Chapter 8.
Remarks
The $Z directive controls the storage size of enumerated types. An enumerated type
declared in the the {$Z+} state is always stored as a word. An enumerated type
declared in the {$Z–} state is stored as a byte if the type has no more than 256
values; otherwise it is stored as a word. The {$Z+} state is useful for interfacing with
C and C++ libraries, which usually represent enumerated types as words.
{$IFDEF CPU87}
{$N+}
type
Real = Double;
{$ELSE}
{$N-}
type
Single = Real;
Double = Real;
Extended = Real;
Comp = Real;
{$ENDIF}
You can nest conditional compilation constructs up to 16 levels deep. For every
{$IFxxx}, the corresponding {$ENDIF} must be found within the same source file--
which means there must be an equal number of {$IFxxx}'s and {$ENDIF}'s in every
source file.
Conditional symbols
Conditional compilation is based on the evaluation of conditional symbols.
Conditional symbols are defined and undefined using the directives
{$DEFINE name}
{$UNDEF name}
You can also use the /D switch in the command-line compiler to define a symbol (or
add the symbol to the Conditional Defines input box on the
Directories/Conditionals page of the Options|Project dialog box in the IDE).
Conditional symbols are best compared to Boolean variables: They are either True
(defined) or False (undefined). The {$DEFINE} directive sets a given symbol to True,
and the {$UNDEF} directive sets it to False.
Conditional symbols follow the same rules as Pascal identifiers: They must start
with a letter, followed by any combination of letters, digits, and underscores. They
can be of any length, but only the first 63 characters are significant.
Conditional symbols and Pascal identifiers have no correlation whatsoever.
Conditional symbols cannot be referenced in the actual program and the program's
identifiers cannot be referenced in conditional directives. For example, the construct
const
Error Messages
C
This chapter lists the possible error messages you can get from Delphi. The error
messages are grouped into two categories, compiler errors and run-time errors.
1 Out of memory
This error occurs when the compiler has run out of memory.
Try these possible solutions:
• Increase the amount of available memory in Windows by closing other
Windows applications or by increasing the swap file size.
• Set the Link Buffer option to Disk on the Linker page of the Options|Project
dialog box.
• If you are using the command-line compiler, use the /L option to place the link
buffer on disk.
2 Identifier expected
An identifier was expected at this point.
You may be trying to redeclare a reserved word or standard directive as a variable.
3 Unknown identifier
This identifier has not been declared, or it may not be visible within the current
scope.
4 Duplicate identifier
Within the current scope, the identifier you are declaring already represents a
program's name, a constant, a variable, a type, a procedure or a function.
5 Syntax error
An illegal character was found in the source text.
You may have omitted the quotes around a string constant.
16 Disk full
The disk on which you are compiling this project is out of space. You must delete
some files or use a different disk.
Note that the symbolic information used by Turbo Debugger and the Browser can
be quite large. If you are not debugging or browsing your files, you can turn these
options off to save disk space.
21 Error in type
This symbol cannot start a type definition.
26 Type mismatch
This error occurs due to one of the following:
• Incompatible types of the variable and expression in an assignment statement.
• Incompatible types of the actual and formal parameter in a call to a procedure
or function.
• An expression type that is incompatible with the index type in array indexing.
• Incompatible operand types in an expression.
• Your typecast is incorrect.
For more information, see “Type Compatibility” on page 25.
31 Constant expected
Only a constant is allowed here. For more information on constants, see Chapter 2.
36 BEGIN expected
A BEGIN was expected here, or there is an error in the block structure of the unit or
program.
37 END expected
An END was expected here, or there is an error in the block structure of the unit or
program.
42 Error in expression
This symbol cannot participate in an expression in the way it is written.
You may have forgotten to write an operator between two operands.
46 Undefined external
The external procedure or function does not have a matching PUBLIC definition in
an object file.
Make sure you have specified all object files in $L filename directives, and checked
the spelling of the procedure or function identifier in the .ASM file.
50 DO expected
The reserved word DO does not appear where it should.
54 OF expected
The reserved word OF does not appear where it should in the case statement.
55 INTERFACE expected
The reserved word INTERFACE does not appear where it should; the reserved
word INTERFACE is missing or declarations appear before the INTERFACE
reserved word.
57 THEN expected
The reserved word THEN does not appear where it should.
58 TO or DOWNTO expected
The reserved word TO or DOWNTO does not appear in the for loop.
59 Undefined FORWARD
Here are some possible sources of this error:
• The procedure or function was declared in the interface part of a unit, but its
body never occurred in the implementation part.
• The procedure or function was declared forward but its definition was never
found.
61 Invalid typecast
Here are some possible sources of this error:
• In a variable typecast, the sizes of the variable reference and the destination type
differ.
• You are attempting to typecast an expression where only a variable reference is
allowed.
• For more information on variable typecasting, see page 34.
62 Division by zero
You are attempting to divide using a constant expression that evaluates to zero. The
compiler can generate this error only when you use a constant expression as the
divisor of a divide operator, and that constant expression evaluates to zero.
Reading:
Read and Readln can input these variables:
• character
• integer
• real
• string
Writing:
Write and Writeln can output these variables:
• character
• integer
• real
• string
• Boolean
73 IMPLEMENTATION expected
The reserved word IMPLEMENTATION must appear between the interface part
and the actual procedure definitions.
83 Invalid @ argument
Valid arguments are variable references and procedure or function identifiers. For
more information on the @ operator, see page 49.
84 UNIT expected
The reserved word unit should appear in the header for the module. If you have
any other reference, such as program or library, you must replace it with unit. If
you are trying to compile this unit as a .DLL you must replace program with library
in the project file.
85 ";" expected
A semicolon does not appear where it should. The line above the highlighted line is
missing a semicolon. All Object Pascal statements are separated by a semicolon.
87 "," expected
A comma does not appear where it should.
88 "(" expected
An opening parenthesis is missing from the selected line.
This error might indicate that the compiler considers the identifier to the left of the
insertion point a type identifier. In this case, the compiler is looking for an opening
parenthesis to make a typecast expression.
89 ")" expected
A closing parenthesis is missing from the selected line.
90 "=" expected
An equal sign is missing from the selected line. The equals sign is a relational
operator used to test equality.
91 ":=" expected
An assignment operator is missing from the selected line. The assignment operator
is used to assign values to variables or properties.
For more information on assignment statements, see page 55.
94 "." expected
A period is missing from the selected line. This indicates that a type is being used as
a variable or that the name of the program itself overrides an important identifier
from another unit.
Global
The total size of the global variables declared within a program or unit cannot
exceed 64K.
Local
The total size of the local variables declared within a procedure or function cannot
exceed 64K.
177 ON expected
To handle a raised exception within an excepts block you must preface the exception
type identifier with the on standard directive. For more information on handling
exceptions, see page 115.
Run-time errors
Certain errors at run time cause the program to display an error message and
terminate:
Run-time error nnn at xxxx:yyyy
where nnn is the run-time error number, and xxxx:yyyy is the run-time error
address (segment and offset).
The run-time errors are divided into four categories: File errors, 1 through 99; I/O
errors, 100 through 149; and fatal errors, 200 through 255.
File errors
18 No more files.
A call to FindFirst or FindNext found no files matching the specified file name and
set of attributes.
Fatal errors
These errors always immediately terminate the program.
Index 279
buffer strings, 7 /E, 221
BufPtr pointer, 167 ChDir procedure, 139 /F, 219
BufSize variable, 167 CheckBreak variable, 145 /G, 221
Build command, 219 CheckEOF variable, 144, 145 /GD, 222
build command-line option, Chr function, 14, 181 /GP, 222
219 class components /GS, 222
built-in assembler private, 90 /I, 221
directives, 187 protected, 90 /L, 219
expressions, 192 public, 89 /M, 219
classes, 198 published, 89 mode, 218
Object Pascal scope, 88 /O, 221
expressions versus, visibility, 89 /Q, 220
193 class domain, 87 switching directive
operators, 192 class forward references, 88 defaults (/$), 217
types, 201 class instances, 86 /T, 220
instruction sizing, 189 class methods, 87, 110 /U, 221
opcodes, 189 class of reserved words, 108 /V, 222
operands, 192 class reference types command-line compiler
procedures and functions, constructors, 109 arguments, 215
203 class references, 86 compiling and linking
reserved words, 192 class reserved word, 88, 110 with, 215
BX register, 173 class type compatibility, 88 extended syntax, 217
Byte data type, 12 class types, 85 options, 215
ByteBool data type, 163 classes 286 code generation,
ancestors, 87 216
C components, 85 align data, 216
descendants, 87 append debug
$C code segment attribute, inheritance, 87 information to EXE,
227 scope, 88 222
call model, 207 class-reference types, 108 Boolean evaluation,
call models, 70 ClassType method function, 216
callbacks 109 build all, 218, 219
smart, 71 Close procedure, 139, 140, build all units, 217
calling 145 debug, 221
methods, 93 CloseFile procedure, 146 debug information,
calling conventions, 171 ClrEol procedure, 144 216, 222
constructors and ClrScr procedure, 144 in EXE, 217
destructors, 176 CmdShow variable, 137 define conditional
calls Code generation symbol, 217
near and far, 173 Windows, 241 DOS real-mode .EXE,
case code segment, 208 217
sensitivity of Turbo attributes, 159 emulation, 216
Pascal, 4 changing, 160 EXE & DCU directory,
statement syntax, 58 maximum size of, 159 221
Char data type, 14, 162 procedures and functions EXE and TPU
character in, 207 directory, 217
arrays, 155 Code segment attribute, 227 find error, 219
pair special symbols, 4 codesegs2, 159 find run-time error,
pointer operators, 46 command-line 217
pointers compiler reference, 215 force far calls, 216
character arrays and, options, 215 I/O checking, 216
155 /B, 219 include directories,
indexing, 155 /D, 218 217, 221
string literals and, 154 debug, 221 link buffer, 218, 219
Index 281
segment, 207, 208 built-in assembler, 187, importing, 132
DLL, 137 203, 204 linking, 129
maximum size, 30 compiler, defined, 8 variables, 30
data alignment, 226 forward, 72 dynamic directive, 92
DATA segment, 207 inline, 73 dynamic methods, 92
DCC.CFG file, 220, 222 list of Borland Pascal, 4 dynamic variables, 21, 33
sample, 223 private, 5 dynamic versus virtual
dead code eliminated, 184 public, 5 methods, 92
Debug Information standard, 4 dynamic-link libraries, 129.
command, 228 directories See also DLL
option, 228 command-line options,
debugging 220 E
command-line option, 222 DISCARDABLE code
information switch, 228 segment attribute, 160 /E command-line option,
options, command-line, div operator, 44 221
221 DLL editing keys
range-checking switch, CmdShow variable, 137 in WinCrt unit, 144
237 contrasted with a unit, eliminate dead code, 184
stack overflow switch, 129 $ELSE compiler directive,
239 data segment, 137 229
decimal notation, 6 exit code, 135 embedding control
default array properties, 105 files in a, 137 characters in strings, 7
default directive, 105, 106 global memory in a, 137 empty set, 21
$DEFINE compiler directive, global variables in, 137 $ENDIF compiler directive,
218, 229, 243 gmem_DDEShare 230
defining properties, 102 attribute, 137 end-of-line character, 3
access specifiers, 103 HeapLimit variable, 138 entry code, 176, 203
storage specifiers, 106 HPrevInst variable, 137 enumerated
DELPHI.DSL, 220, 221 initalization code, 135 types, 14, 163
DEMANDLOAD code KERNEL, 130 Eof function, 139
segment attribute, 160 multi-language Eoln function, 139
descendants programming, 129 Erase procedure, 139
defined, 87 PrefixSeg variable, 137 error messages
Description directive, 229 run-time errors in a, 138 searching, 219
designators structure of, 133 ErrorAddr variable, 179
field, 33 syntax, 133 errors
method, 33 unloading a, 136 range, 237
destorying objects using a, 130 reporting, 178
nil values, 97 domain examples
Destroy method, 97 classes, 87 array type, 18
Destroy method versus Free DoneWinCrt procedure, 143, character strings, 7
method, 97 144 constant expressions, 9,
destroying objects, 96 double address-of (@@) 10
destructor reserved word, 94 operator, 53 control characters in
destructors, 96 Double data type, 16, 150, strings, 7
calling conventions, 176 164 enumerated type, 15
defined, 87 drivers record type, 19
devices text-file device, 145 subrange type, 15
drivers, 145 DS register, 178 using subrange types, 15
digit syntax diagram, 3 DSEG segment, 207 variant part of a record,
digits, defined, 3 DSL & CFG directory 20
direct memory, 168 command-line option, 220 EXE & DCU directory
directives DX register, 173 command-line option, 221
assembler, defined, 190 dynamic .EXE files
Index 283
/GD command-line option, indexed properties, 104
222 I indexes in arrays, 18
GetDir procedure, 139 $I compiler directive, 140, indexing character pointers,
GetMem procedure, 33 233 155
global I/O, 139 indirect unit references, 126
heap in Windows, 161 checking, 233 infinite loop. See loop,
memory in a DLL, 137 command, 233 infinite
variables in a DLL, 137 devices, 145 inherited reserved word, 88,
gmem_DDEShare attribute, error-checking, 140, 233 94
137, 138 /I command-line option, 221 initialization
gmem_Moveable attribute, identifiers code in DLL, 135
138 as labels, 7 part of a unit, 125
goto statement syntax, 56 defined, 5 initialized variables, 35
GotoXY procedure, 144 examples, 5 in assembler, 207
/GP command-line option, how identified in InitWinCrt procedure, 143,
222 manuals, 5 144
Group unit segments length of, 5 inline
compiler directive, 231 qualified, 5 directive, 73
restrictions on naming, 5 directives, 213
scope of, 11 statements, 211
H if statement syntax, 57 InOut function, 146
Halt procedure, 178 $IFDEF compiler directive, Input text-file variable, 141
handles 232 and WinCrt unit, 142
file, 166 $IFNDEF compiler directive, in WinCrt unit, 141
heap 232 instances, 86
management $IFOPT compiler directive, instruction opcodes, built-in
Windows, 161 232 assembler, 189
fragmenting, 161 immediate values, built-in Integer data type, 12, 162
management, sizes, 218 assembler, 198 integer types, 14
manager implementation part interface part of a unit, 124,
Windows, 161 of a unit, 125, 174 174
allocating memory syntax, 125 interfacing=page, 208
blocks, 162 import units, 131 internal data formats, 162
Windows global, 161 importing procedures and invalid
Windows local, 161 functions built-in assembler, 196
changing size of, 161 dynamically, 132 IOResult function, 139, 140
size of, 161 statically, 132
heap management, sizes, 235 with import unit, 131
HeapAllocFlags variable, in operator, 47, 49
J
138, 162 InactiveTitle variable, 145 jump sizing, automatic,
HeapBlock variable, 162 Include directories built-in assembler, 189
HeapLimit variable, 162 command-line option, 221,
heapmanwin, 161
hex digits, 3
233 K
include files, 221, 233
hexadecimal $K compiler directive, 239
including resources, 237
constants, 6 KERNEL dynamic-link
index
numbers, 6 library, 130
clause, 130, 135
Hi function, 181 KeyPressed function, 144
syntax, 32
high bounds of index type of types valid in arrays, 18
an array, 18 index directive, 105 L
High function, 12, 18, 79 index parameter list $L compiler directive, 207,
highest value in a range, 12 array properties, 104 208, 211, 233, 234
HInstance variable, 137 index specifiers $L filename compiler
host type, 15 property definitions, 105 directive, 73, 152
Index 285
component designators, string, 46 passing parameters
33 structure member by reference, 171
directories, compiler selector, 198 by value, 171
directive, 233 types of, 44 passing parmeters, 171
files, 207 unary arithmetic, 44 passing string variables of
files, linking with, 233 xor, 45 varying sizes, 17
object directories command- or operator, 45 PChar data type, 22
line option, 221 Ord function, 12, 14, 15, 181 PERMANENT code segment
object reference, 86 order of evaluation, 183 attribute, 160
object types. See also objects ordering between two string- pointer (^) symbol, 21, 22, 33
objects type values, 17 Pointer data type, 22
creating, 95 ordinal pointers
defined, 86 types comparing, 48
destroying, 96 predefined, 12 types, 21, 165
fields, 87 user-defined, 12 values, 33
designators, 33 ordinality variables, 33
files in $L directive, 208 defined, 12 pointer-type constants, 39
methods, 87 enumerated constant, 15 Port array, 169
referencing, 86 finding enumerated ports, accessing, 169
scope, 67 type's value, 15 PortW array, 169
Odd function, 181 returning, 12 pound (#) character, 7
opcomp, 181 Char values, 14 precedence of operators, 41,
Open function, 146 Origin variable, 145 44
open parameters, 76, 78 Output text-file precision
array, 19, 76, 79 in WinCrt unit, 141 of real-type values, 16
how passed, 172 Output text-file variable, 141 rules of arithmetic, 13
string, 17, 78 and WinCrt unit, 142 Pred function, 12, 181
open string parameters overflow checking, 236 predecessor of a value,
compiler switch, 235 override directive, 91 returning, 12
OpenString identifier, 17 overriding methods, 91 PrefixSeg variable, 137
operands, 41, 192 overriding properties, 107 PRELOAD code segment
built-in assembler, 192 attribute, 160
operators
@ (address-of), 21, 34, 49,
P private
directive, 5
52 $P compiler directive, 78, procedures and functions,
@@ (double address-of), 235 125
53 packed private sections
and, 45 reserved word, 17 classes, 90
arithmetic, 44 string type, 18 PROC directive, 208
binary arithmetic, 44 strings, comparing, 48 procedural
bitwise, 45 parameters types
Boolean, 45 actual, 56 type compatibility of,
character-pointer, 46 constant, 76 24
div, 44 floating-point, 172 variable typecasts, 35
logical, 45 formal, 56 procedural-type constants,
mod, 45 open, 78 40
not, 45 array, 79 procedure
or, 45 string, 78 declaration syntax, 69
precedence of, 41, 44 passing, 56 declarations
built-in assembler, 201 types, 75 assembler, 72
relational, 47 untyped, 77 external, 72
set, 47 value, 76, 172 forward, 72
shl, 45 variable, 77 inline, 73
shr, 45 virtual method, 176 headings, 70
Index 287
the ordinality of a value, separating tokens, 3 single characters listed, 4
12 separators, defined, 3 SS register, 178
the predecessor of a set stack, 161
value, 12 constructors 80x87, 152
the successor of a value, syntax, 50 changing size of, 161
12 membership checking, 137
Rewrite procedure, 139, 140 testing, 49 checking switch directive,
right operators, 47 239
brace special symbol, 4 types, 20, 165 frame, built-in assembler
bracket special symbol, 4 set constructors, 42 use of, 203
RmDir procedure, 139 sets. See also set overflow, 30
Round function, 181 comparing, 49 switch directive, 239
rules small, 183 passing parameters and,
governing boolean SetTextBuf procedure, 140 171
variables, 14 set-type constants, 39 segment, 30
run-time Shift instructions faster than DLLs and, 138
errors, 178 multiply or divide, 184 size, 235
in a DLL, 138 shl operator, 45 Stack Checking
run-time errors short-circuit Boolean command, 239
Find Error command and, evaluation, 46, 182, 227 option, 239
219 Shortint data type, 12 standard, 139
finding, 219 shr operator, 45 directives, 4
run-time type information, signed number syntax procedure or function
89 diagram, 6 used as a procedural
significand, 163 value, 24
S simple statement part syntax, 66
expression syntax, 43 statements, 55
$S compiler directive, 30, statement syntax, 55 assignment, 55
137, 238, 239 types case, 58
scale factor syntax diagram, comparing, 48 compound, 57
6 simple-type constants, 36 conditional, 57
scope single character special for, 61
block, 66 symbols, 4 goto, 56
class, 88 Single data type, 16, 150, 164 if, 57
object, 67 size procedure, 56
record, 67 of a given string, finding, repeat, 59
rules of, 66 17 repetitive, 59
type identifiers, 11 of structured types, simple, 55
unit, 67 maximum, 17 structured, 57
within methods, 94 SizeOf function, 79 while, 60
ScreenSize variable, 143, 145 small sets, 183 with, 62
ScrollTo procedure, 144 smart callbacks, 71, 239 static
Seek procedure, 139, 140 smart linking, 185 data area, 161
SeekEof function, 139 software importing, 132
SeekEoln function, 140 floating-point linking, 129
segment model, 16 static methods, 90
attributes, 159 restrictions, 16 storage specifiers, 106
sub-allocator, 161 source debugging compiler stored directive, 106
Segment size compiler switch, 228 storing
directive, 238 SP register, 178 null-terminated strings,
segments, 207 space characters, 3 19
Self identifer, 87 special symbols strict string parameter
Self identifier, 110 built-in assembler, 196 checking, 241
Self parameter, 176 character pairs listed, 4 string. See also strings
Index 289
record, 19, 166 value wep_Free_DLL value for
set, 20, 165 parameters, 76, 172 ExitCode, 136
Shortint, 12 typecast syntax, 51 wep_System_Exit value for
Single, 12, 150 var ExitCode, 136
string, 17, 165 declaration section, 185 WhereX function, 144
subrange, 15 parameters, 77, 171 WhereY function, 144
Text, 141 built-in assembler and, while statement syntax, 60
Word, 12 197 wincrt, 142
WordBool, 14, 163 string checking WinCrt unit, 141, 142
compiler switch, 241 editing keys in, 144
U variable. See also variables
declaration part syntax,
using the, 142
variables in, 144
/U command-line option, 66 windows stack frames, 241
221 declaration syntax, 29 Windows symbols, 244
unary parameters, 77 WindowSize variable, 145
arithmetic operators, 44 reference WindowTitle variable, 145
operands, 41 qualifiers, 31 WinOrg variable, 144
$UNDEF compiler directive, syntax, 31 with statement syntax, 62
241, 243 typecasts, 34 with statements, 88, 93
Unit Directories and procedural types, word alignment, automatic,
option, 221 35 184
Unit segment grouping syntax, 34 Word data type, 12
compiler directive, 231 variables WordBool data type, 14, 163
unit syntax, 124 absolute, 30 write directive, 102
units array, 32 Write procedure, 140
heading, 124 declarations, 29 for values not of type
identifiers, 5 dynamic, 21, 33 Char, 141
implementation part, 125 global, 30 WriteBuf procedure, 144
import, 131 initialized in assembler, WriteChar procedure, 144
indirect references, 126 207 Writeln procedure, 140
initialization part, 125 initializing, 35
interface part, 124
scope of, 67
local, 30
parameters, 171
X
Strings, 153 pointer, 33 $X compiler directive, 7, 19,
unit directories option, record, 33 22, 31, 46, 230
221 references, 31 xor operator, 45
uses clause, 123 string, 32
version number, 126
WinCrt, 142
variant part of records, 19 Y
VER80 symbol, 244
unsigned $Y compiler directive, 240
virtual
constant syntax, 42 method
integer syntax diagram, 6 parameter, 176 Z
number syntax diagram, virtual directive, 91 zero-based character arrays,
6 virtual methods, 91 37, 153, 155
real syntax diagram, 6 virtual versus dyanmic
untyped methods, 92
files, 142, 166 visibility of class
parameters, 77 components, 89
uses clause, 123
V W
$W compiler directive, 241
$V compiler directive, 241 WEP exported function, 136
/V command-line option,
222
Copyright © 1995 Borland International. All rights reserved. All Borland products are trademarks or registered trademarks of
Borland International, Inc. Other brand and product names are trademarks or registered trademarks of their respective
holders.
1E0R395
9596979899-9 8 7 6 5 4 3 2 1
W1
Contents
Introduction 1 Procedural type compatibility.................24
What’s in this manual? .................................. 1 Identical and compatible types....................24
Syntax Diagrams ............................................ 1 Type identity.............................................25
Type compatibility ...................................25
Chapter 1 Assignment compatibility........................26
Tokens 3 The type declaration part .............................27
Special symbols .............................................. 3
Reserved words and standard directives .... 4 Chapter 4
Identifiers........................................................ 5 Variables and typed constants 29
Numbers ......................................................... 6 Variable declarations ....................................29
Labels .............................................................. 6 The data segment .....................................30
Character strings ............................................ 7 The stack segment ....................................30
Comments....................................................... 7 Absolute variables....................................30
Program lines ................................................. 8 Variable references ...................................31
Chapter 2 Qualifiers...................................................32
Arrays, strings, and indexes 32
Constants 9 Records and field designators 33
Chapter 3 Object component designators 33
Pointers and dynamic variables 33
Types 11 Variable typecasts ....................................34
Simple types ................................................. 11 Typed constants ............................................35
Ordinal types ........................................... 12 Simple-type constants ..............................36
Integer types 12 String-type constants ...............................37
Boolean types 14 Structured-type constants........................37
Char type 14 Array-type constants 37
Enumerated types 14 Record-type constants 38
Subrange types 15 Set-type constants 39
Real types ................................................. 16 Pointer-type constants .............................39
80x87 floating point 16 Procedural-type constants .......................40
Software floating point 16
String types................................................... 17 Chapter 5
Structured types........................................... 17
Array types .............................................. 18
Expressions 41
Expression syntax .........................................41
Record types ............................................ 19 Operators.......................................................44
Set types ................................................... 20 Arithmetic operators................................44
File types .................................................. 21 Logical operators......................................45
Pointer types................................................. 21
Boolean operators 45
Type Pointer............................................. 22
String operator..........................................46
Type PChar .............................................. 22
Character-pointer operators ....................46
Procedural types .......................................... 22
Set operators .............................................47
Global procedure pointers ...................... 22
Relational operators .................................47
Method pointers ...................................... 23
Comparing simple types 48
Procedural values .................................... 23
Contents i
Comparing strings 48 Export declarations ..................................71
Comparing packed strings 48 cdecl declarations.....................................71
Comparing pointers and references 48 Forward declarations...............................72
Comparing character pointers 48 External declarations ...............................72
Comparing sets 49 Assembler declarations............................73
Testing set membership 49 Inline declarations ....................................74
Class operators ........................................ 49 Function declarations ...................................74
The @ operator......................................... 49 Parameters.....................................................75
@ with a variable 49 Value parameters .....................................76
@ with a procedure, function, or Constant parameters................................76
method 50 Variable parameters .................................77
Function calls................................................ 50 Untyped parameters ................................77
Set constructors ............................................ 50 Open parameters......................................78
Value typecasts............................................. 51 Open-string parameters 78
Procedural types in expressions ................. 52 Open-array parameters 79
Open-array constructors ..............................81
Chapter 6 Type variant open-array parameters ..........81
Statements 55
Simple statements ........................................ 55 Chapter 9
Assignment statements ........................... 55 Class types 85
Procedure statements.............................. 56 Instances and references...............................86
Goto statements ....................................... 56 Class components .........................................87
Structured statements.................................. 57 Fields .........................................................87
Compound statements ............................ 57 Methods ....................................................87
Conditional statements ........................... 57 Properties..................................................87
If statements 57 Inheritance.....................................................87
Case statements 58 Components and scope ................................88
Repetitive statements .............................. 59 Forward references.......................................88
Repeat statements 60 Class type compatibility rules......................88
While statements 60 Component visibility ....................................89
For statements 61 Public components ...................................89
With statements ....................................... 62 Published components.............................89
Protected components .............................90
Chapter 7 Private components..................................90
Blocks, locality, and scope 65 Static methods...............................................90
Blocks ............................................................ 65 Virtual methods ............................................91
Rules of scope............................................... 66 Dynamic methods.........................................92
Abstract methods..........................................92
Block scope............................................... 66
Method activations .......................................93
Record scope............................................ 67 Method implementations .............................93
Class scope ............................................... 67 Constructors and destructors ......................94
Unit scope ................................................ 67 Constructors .............................................95
Chapter 8 Destructors ...............................................96
Class operators..............................................98
Procedures and functions 69 The is operator..........................................98
Procedure declarations ................................ 69 The as operator.........................................98
Near and far declarations ....................... 70 Message handling .........................................99
Contents iii
Null-terminated strings and standard Method calling conventions ..................175
procedures ........................................... 157 Constructors and destructors................176
Entry and exit code ................................176
Chapter 16 Register-saving conventions..................178
Memory issues 159 Exit procedures ...........................................178
Windows memory management............... 159
Code segments....................................... 159 Chapter 18
Segment attributes 159 Optimizing your code 181
MOVEABLE or FIXED................. 159 Constant folding .........................................181
PRELOAD or DEMANDLOAD.. 160 Constant merging .......................................181
DISCARDABLE or PERMANENT160 Short-circuit evaluation..............................182
Changing attributes 160 Constant parameters ..................................182
The automatic data segment................. 160 Redundant pointer-load elimination.........182
The heap manager ................................. 161 Constant set inlining...................................183
Internal data formats ................................. 162 Small sets .....................................................183
Order of evaluation ....................................183
Integer types .......................................... 162
Range checking ...........................................184
Char types .............................................. 162 Shift instead of multiply or divide ............184
Boolean types......................................... 163 Automatic word alignment........................184
Enumerated types.................................. 163 Eliminating dead code................................184
Floating-point types .............................. 163 Smart linking...............................................185
The Real type 163
The Single type 164 Chapter 19
The Double type 164 The built-in assembler 187
The Extended type 164 The asm statement ......................................187
The Comp type 165 Register use.............................................188
Pointer types .......................................... 165 Assembler statement syntax ......................188
String types ............................................ 165 Labels ......................................................188
Set types ................................................. 165 Instruction opcodes................................189
Array types ............................................ 166 RET instruction sizing 189
Record types .......................................... 166 Automatic jump sizing 189
File types ................................................ 166 Assembler directives..............................190
Procedural types.................................... 167 Operands.................................................192
Class types ............................................. 167 Expressions..................................................193
Class reference types............................. 168 Differences between Object Pascal and
Direct memory access................................ 168 Assembler expressions ........................193
Direct port access ....................................... 169 Expression elements...............................194
Constants 194
Chapter 17 Numeric constants ........................194
Control issues 171 String constants .............................194
Calling conventions ................................... 171 Registers.........................................195
Variable parameters .............................. 171 Symbols 196
Value and constant parameters ............ 172 Expression classes ..................................198
Open parameters ................................... 172 Expression types.....................................200
Function results ..................................... 173 Expression operators .............................201
NEAR and FAR calls............................. 173 Assembler procedures and functions........203
Nested procedures and functions ........ 174
Appendix B
Index 277
Compiler directives 225
Align data ................................................... 226
Boolean evaluation..................................... 227
Code segment attribute ............................. 227
Debug information..................................... 228
DEFINE directive....................................... 229
Description ................................................. 229
ELSE directive ............................................ 229
ENDIF directive ......................................... 230
Contents v
vi Object Pascal Language Definition
Tables
1-1 Object Pascal reserved words............. 4 13-1 Input and output procedures and
1-2 Object Pascal directives....................... 5 functions............................................139
3-1 Fundamental integer types ............... 13 13-2 Special characters in the WinCrt
3-2 Generic integer types for 16-bit window .............................................143
implementations of Object Pascal..... 13 13-3 WinCrt procedures and functions...144
3-3 Generic integer types for 32-bit 13-4 WinCrt variables...............................144
implementations of Object Pascal..... 13 16-1 Virtual Method Table layout ...........168
3-4 Real data types................................... 16 19-1 Built-in assembler reserved words .192
5-1 Precedence of operators .................... 41 19-2 String examples and their values ....195
5-2 Binary arithmetic operations ............ 44 19-3 CPU registers ....................................195
5-3 Unary arithmetic operations............. 44 19-4 Values, classes, and types of
5-4 Logical operations.............................. 45 symbols..............................................197
5-5 Boolean operations ............................ 45 19-5 Predefined type symbols..................201
5-6 String operation ................................. 46 19-6 Summary of built-in asssembler
5-7 Permitted PChar constructs .............. 47 expression operators ........................201
5-8 Set operations..................................... 47 19-7 Definitions of built-in assembler
5-9 Relational operations......................... 47 expression operators ........................202
8-1 Type variant open-array expressions83 A-1 Command-line options ....................216
10-1 Predefined exception classes........... 121 B-1 TestFDIV values ...............................237
10-2 Exception support routines............. 122
Contents 1
2 Object Pascal Language Guide