Delphi Language Guide Borland® Delphi ™ Borland Software Corporation 100 Enterprise Way, Scotts Valley, CA 95066-3249 http://www.borland.
Refer to the DEPLOY document located in the root directory of your product for a complete list of files that you can distribute in accordance with the License Statement and Limited Warranty. Borland Software Corporation may have patents and/or pending patent applications covering subject matter in this document. Please refer to the product CD or the About dialog box for the list of applicable patents. The furnishing of this document does not give you any license to these patents.
Contents Chapter 1 Chapter 4 Introduction What’s in this manual? . . . . Using Delphi . . . . . . . . Typographical conventions Other sources of information . Software registration and technical support . . . . . . . 1-1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Syntactic elements 1-1 1-1 1-2 1-2 Fundamental syntactic elements . . Special symbols . . . . . . . . . . Identifiers . . . . . . . . . . . . . Qualified identifiers . . . . . . Reserved words . .
Case statements . . Control loops . . . Repeat statements. While statements . For statements. . . Blocks and scope . . . . . Blocks . . . . . . . . . Scope. . . . . . . . . . Naming conflicts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-25 . 4-27 . 4-27 . 4-27 . 4-28 . 4-29 . 4-30 . 4-30 . 4-31 Procedural types . . . .
String parameters. . . . . . . . . . Array parameters. . . . . . . . . . Open array parameters. . . . . Variant open array parameters Default parameters . . . . . . . . . Default parameters and overloaded routines. . . . . . Default parameters in forward and interface declarations . . Calling procedures and functions . . Open array constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-15 . 6-16 . 6-16 . 6-18 . 6-19 Class references . . . . . . . . . . . . . . . .
Exceptions and runtime errors in libraries . . . . . . . . . . . Shared-memory manager (Windows only) . . . . . . . . . . . . Packages . . . . . . . . . . . . . . . . . . Package declarations and source files Naming packages . . . . . . . . . . The requires clause . . . . . . . . . The contains clause . . . . . . . . . Compiling packages . . . . . . . . . . Generated files . . . . . . . . . . . Package-specific compiler directives . . . . . . . . . . . . . . Package-specific command-line compiler switches .
Chapter 13 Inline assembly code The asm statement . . . . . . . . Register use . . . . . . . . . . Assembler statement syntax . . Labels . . . . . . . . . . . . . Instruction opcodes . . . . . RET instruction sizing . . Automatic jump sizing . . Assembly directives . . . . . Operands . . . . . . . . . . . Expressions . . . . . . . . . . . . Differences between Delphi and assembler expressions Expression elements . . . . . . . Constants . . . . . . . . . . . . Registers . . . . . . . . . . . . Symbols . .
Tables 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 4.11 4.12 5.1 5.2 5.3 5.4 5.5 5.6 5.7 6.1 8.1 Equivalent symbols . . . . . . . Reserved words . . . . . . . . . Directives . . . . . . . . . . . . Binary arithmetic operators . . Unary arithmetic operators . . . Boolean operators . . . . . . . . Logical (bitwise) operators . . . String operators . . . . . . . . . Character-pointer operators . . Set operators . . . . . . . . . . . Relational operators . . . . . . . Precedence of operators. . . . .
Chapter 1 Introduction Chapter1 This manual describes the Delphi programming language as it is used in Borland development tools. What’s in this manual? The first seven chapters describe most of the language elements used in ordinary programming. Chapter 8 summarizes standard routines for file I/O and string manipulation. The next chapters describe language extensions and restrictions for dynamic-link libraries and packages (Chapter 9), and for object interfaces (Chapter 10).
Other sources of information This manual generally assumes that you are working in the IDE and that you are building applications that use the Borland Component Library (CLX). Occasionally, however, Delphi-specific rules are distinguished from rules that apply to Object Pascal programming. Typographical conventions Identifiers—that is, names of constants, variables, types, fields, properties, procedures, functions, programs, units, libraries, and packages—appear in italics in the text.
Software registration and technical support Software registration and technical support Borland Software Corporation offers a range of support plans to fit the needs of individual developers, consultants, and corporations. To receive help with this product, return the registration card and select the plan that best suits your needs. For additional information about technical support and other Borland services, contact your local sales representative or visit us online at http://www.borland.com/.
1-4 Delphi Language Guide
Part I Basic language description Part I The chapters in Part I present the essential language elements required for most programming tasks.
Chapter 2 Overview Chapter2 Delphi is a high-level, compiled, strongly typed language that supports structured and object-oriented design. Based on Object Pascal, its benefits include easy-to-read code, quick compilation, and the use of multiple unit files for modular programming. Delphi has special features that support Borland’s component framework and RAD environment. For the most part, descriptions and examples in this manual assume that you are using Borland development tools.
Program organization Delphi source files The compiler expects to find Delphi source code in files of three kinds: • unit source files (which end with the .pas extension) • project files (which end with the .dpr extension) • package source files (which end with the .dpk extension) Unit source files typically contain most of the code in an application.
Example programs Various tools in the IDE store data in files of other types. Desktop settings (.dsk or .desk) files contain information about the arrangement of windows and other configuration options; desktop settings can be project-specific or environment-wide. These files have no direct effect on compilation. Compiler-generated files The first time you build an application or a dynamically linkable library, the compiler produces a compiled unit (.dcu on Windows, and .dcu or .
Example programs The first line declares a program called Greeting. The {$APPTYPE CONSOLE} directive tells the compiler that this is a console application, to be run from the command line. The next line declares a variable called MyMessage, which holds a string. (Delphi has genuine string data types.) The program then assigns the string “Hello world!” to the variable MyMessage, and sends the contents of MyMessage to the standard output using the Writeln procedure.
Example programs defined in Unit1. Here’s the source code for Unit1, which must be saved in a file called Unit1.pas: unit Unit1; interface procedure PrintMessage(msg: string); implementation procedure PrintMessage(msg: string); begin Writeln(msg); end; end. Unit1 defines a procedure called PrintMessage that takes a single string as an argument and sends the string to the standard output. (In Pascal, routines that do not return a value are called procedures.
Example programs begin { calls to Application } Application.Initialize; Application.CreateForm(TForm1, Form1); Application.CreateForm(TForm2, Form2); Application.Run; end. Once again, our program is called Greeting. It uses three units: QForms, which is part of CLX; Unit1, which is associated with the application’s main form (Form1); and Unit2, which is associated with another form (Form2).
Example programs Unit1 creates a class named TForm1 (derived from TForm) and an instance of this class, Form1. TForm1 includes a button—Button1, an instance of TButton—and a procedure named TForm1.Button1Click that is called at runtime whenever the user presses Button1. TForm1.Button1Click hides Form1 and it displays Form2 (the call to Form2.ShowModal). NOTE In the previous example, Form2.ShowModal relies on the use of auto-created forms.
When the Greeting program starts, Form1 is displayed and Form2 is invisible. (By default, only the first form created in the project file is visible at runtime. This is called the project’s main form.) When the user presses the button on Form1, Form2, displays the “Hello world!” greeting. When the user presses the CancelButton or the Close button on the title bar, Form2 closes.
Chapter 3 Programs and units Chapter3 A program is constructed from source-code modules called units. Each unit is stored in its own file and compiled separately; compiled units are linked to create an application. Units allow you to • divide large programs into modules that can be edited separately. • create libraries that you can share among programs. • distribute libraries to other developers without making the source code available.
Program structure and syntax The following example shows the project file for a program called Editor. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 program Editor; uses QForms, {cross-platform Form} REAbout in 'REAbout.pas' {AboutBox}, REMain in 'REMain.pas' {MainForm}; {$R *.res} begin Application.Title := 'Text Editor'; Application.CreateForm(TMainForm, MainForm); Application.Run; end. Line 1 contains the program heading. The uses clause is on lines 3 through 6.
Unit structure and syntax statements are simply method calls to the project’s Application object (most projects have an Application variable that holds an instance of TApplication, TWebApplication, or TServiceApplication). The block can also contain declarations of constants, types, variables, procedures, and functions; these declarations must precede the statement part of the block.
Unit structure and syntax Unit names must be unique within a project. Even if their unit files are in different directories, two units with the same name cannot be used in a single program. The interface section The interface section of a unit begins with the reserved word interface and continues until the beginning of the implementation section.
Unit references and the uses clause So, for example, if you have defined data structures that need to be initialized, you can do this in the initialization section. For units in the interface uses list, the initialization sections of units used by a client are executed in the order in which the units appear in the client’s uses clause. The finalization section The finalization section is optional and can appear only in units that have an initialization section.
Unit references and the uses clause If such an explicit reference appears in the project file, other source files can refer to the unit with a simple uses clause that does not need to match case: uses Myunit; For more information about the placement and content of the uses clause, see “Multiple and indirect unit references” on page 3-7 and “Circular unit references” on page 3-8.
Unit references and the uses clause In the uses clause of a unit, you cannot use in to tell the compiler where to find a source file. Every unit must be in the compiler’s search path. Moreover, unit names must match the names of their source files. Multiple and indirect unit references The order in which units appear in the uses clause determines the order of their initialization (see “The initialization section” on page 3-4) and affects the way identifiers are located by the compiler.
Unit references and the uses clause Circular unit references When units reference each other directly or indirectly, the units are said to be mutually dependent. Mutual dependencies are allowed as long as there are no circular paths connecting the uses clause of one interface section to the uses clause of another. In other words, starting from the interface section of a unit, it must never be possible to return to that unit by following references through interface sections of other units.
Chapter 4 Syntactic elements Chapter4 The Delphi Language uses the ASCII character set, including the letters A through Z and a through z, the digits 0 through 9, and other standard characters. It is not casesensitive. The space character (ASCII 32) and the control characters (ASCII 0 through 31—including ASCII 13, the return or end-of-line character) are called blanks. Fundamental syntactic elements, called tokens, combine to form expressions, declarations, and statements.
Fundamental syntactic elements Special symbols Special symbols are non-alphanumeric characters, or pairs of such characters, that have fixed meanings. The following single characters are special symbols. # $ & ' ( ) * + , – . / : ; < = > @ [ ] ^ { } The following character pairs are also special symbols. (* (. *) .) .. // := <= >= <> Table 4.1 Equivalent symbols Special symbol Equivalent symbols [ (. ] .
Fundamental syntactic elements Qualified identifiers When you use an identifier that has been declared in more than one place, it is sometimes necessary to qualify the identifier. The syntax for a qualified identifier is identifier1.identifier2 where identifier1 qualifies identifier2. For example, if two units each declare a variable called CurrentValue, you can specify that you want to access the CurrentValue in Unit2 by writing Unit2.CurrentValue Qualifiers can be iterated. For example, Form1.Button1.
Fundamental syntactic elements Directives Directives are words that are sensitive in specific locations within source code. Directives have special meanings in the Delphi language, but, unlike reserved words, appear only in contexts where user-defined identifiers cannot occur. Hence— although it is inadvisable to do so—you can define an identifier that looks exactly like a directive. Table 4.
Fundamental syntactic elements Labels A label is a standard Delphi language identifier with the exception that, unlike other identifiers, labels can start with a digit. Numeric labels can include no more than ten digits—that is, a numeral between 0 and 9999999999. Labels are used in goto statements. For more information about goto statements and labels, see “Goto statements” on page 4-20.
Comments and compiler directives Comments and compiler directives Comments are ignored by the compiler, except when they function as separators (delimiting adjacent tokens) or compiler directives. There are several ways to construct comments: { Text between a left brace and a right brace constitutes a comment. } (* Text between a left-parenthesis-plus-asterisk and an asterisk-plus-right-parenthesis also constitutes a comment.
Expressions The operators @, not, and ^ are unary (taking one operand). All other operators are binary (taking two operands), except that + and – can function as either a unary or binary operator. A unary operator always precedes its operand (for example, -B), except for ^, which follows its operand (for example, P^). A binary operator is placed between its operands (for example, A = 7). Some operators behave differently depending on the type of data passed to them.
Expressions • The mod operator returns the remainder obtained by dividing its operands. In other words, x mod y = x – (x div y) * y. • A runtime error occurs when y is zero in an expression of the form x/y, x div y, or x mod y. Boolean operators The Boolean operators not, and, or, and xor take operands of any Boolean type and return a value of type Boolean. Table 4.
Expressions Use the $B compiler directive to control evaluation mode. The default state is {$B–}, which enables short-circuit evaluation. To enable complete evaluation locally, add the {$B+} directive to your code. You can also switch to complete evaluation on a project-wide basis by selecting Complete Boolean Evaluation in the Compiler Options dialog (all source units will need to be recompiled).
Expressions The following rules apply to string concatenation. • The operands for + can be strings, packed strings (packed arrays of type Char), or characters. However, if one operand is of type WideChar, the other operand must be a long string (AnsiString or WideString). • The result of a + operation is compatible with any string type. However, if the operands are both short strings or characters, and their combined length is greater than 255, the result is truncated to the first 255 characters.
Expressions Set operators The following operators take sets as operands. Table 4.10 Operator Set operators Operation Operand types Result type Example + union set set Set1 + Set2 – difference set set S - T * intersection set set S * T <= subset set Boolean Q <= MySet >= superset set Boolean S1 >= S2 = equality set Boolean S2 = MySet <> inequality set Boolean MySet <> S1 in membership ordinal, set Boolean A in Set1 The following rules apply to +, –, and *.
Expressions For most simple types, comparison is straightforward. For example, I = J is True just in case I and J have the same value, and I <> J is True otherwise. The following rules apply to relational operators. • Operands must be of compatible types, except that a real and an integer can be compared. • Strings are compared according to the ordinal values that make up the characters that make up the string. Character types are treated as strings of length 1.
Expressions • When @ is applied to a method defined in a class, the method identifier must be qualified with the class name. For example, @TMyClass.DoSomething points to the DoSomething method of TMyClass. For more information about classes and methods, see Chapter 7, “Classes and objects”. Note When using the @ operator, it is not possible to take the address of an interface method as the address is not known at compile time and cannot be extracted at runtime.
Expressions Without parentheses, however, the compiler follows operator precedence rules and reads it as (X = (Y or X)) = Z which results in a compilation error unless Z is Boolean. Parentheses often make code easier to write and to read, even when they are, strictly speaking, superfluous. Thus the first example could be written as X + (Y * Z) Here the parentheses are unnecessary (to the compiler), but they spare both programmer and reader from having to think about operator precedence.
Expressions Examples of set constructors: [red, green, MyColor] [1, 5, 10..K mod 12, 23] ['A'..'Z', 'a'..'z', Chr(Digit + 48)] For more information about sets, see “Sets” on page 5-18. Indexes Strings, arrays, array properties, and pointers to strings or arrays can be indexed. For example, if FileName is a string variable, the expression FileName[3] returns the third character in the string denoted by FileName, while FileName[I + 1] returns the character immediately after the one indexed by I.
Expressions Variable typecasts You can cast any variable to any type, provided their sizes are the same and you do not mix integers with reals. (To convert numeric types, rely on standard functions like Int and Trunc.) Examples of variable typecasts include Char(I) Boolean(Count) TSomeDefinedType(MyVariable) Variable typecasts can appear on either side of an assignment statement. Thus var MyChar: char; ƒ Shortint(MyChar) := 122; assigns the character z (ASCII 122) to MyChar.
Declarations and statements In this example, TByteRec is used to access the low- and high-order bytes of a word, and TWordRec to access the low- and high-order words of a long integer. You could call the predefined functions Lo and Hi for the same purpose, but a variable typecast has the advantage that it can be used on the left side of an assignment statement. For information about typecasting pointers, see “Pointers and pointer types” on page 5-27.
Declarations and statements Hinting Directives The “hint” directives platform, deprecated, and library may be appended to any declaration. These directives will produce warnings at compile time. Hint directives can be applied to type declarations, variable declarations, class and structure declarations, field declarations within classes or records, procedure, function and method declarations, and unit declarations.
Declarations and statements Simple statements A simple statement doesn’t contain any other statements. Simple statements include assignments, calls to procedures and functions, and goto jumps.
Declarations and statements When you use a function call in this way, its return value is discarded. For more information about procedures and functions, see Chapter 6, “Procedures and functions”. Goto statements A goto statement, which has the form goto label transfers program execution to the statement marked by the specified label. To mark a statement, you must first declare the label.
Declarations and statements ƒ {code to execute if no answer is found } Exit; FoundAnAnswer: ƒ { code to execute when an answer is found } end; Notice that we are using goto to jump out of a nested loop. Never jump into a loop or other structured statement, since this can have unpredictable effects. Structured statements Structured statements are built from other statements. Use a structured statement when you want to execute other statements sequentially, conditionally, or repeatedly.
Declarations and statements Compound statements are essential in contexts where Delphi syntax requires a single statement. In addition to program, function, and procedure blocks, they occur within other structured statements, such as conditionals or loops.
Declarations and statements This is equivalent to if OrderDate.Month = 12 then begin OrderDate.Month := 1; OrderDate.Year := OrderDate.Year + 1; end else OrderDate.Month := OrderDate.Month + 1; If the interpretation of obj involves indexing arrays or dereferencing pointers, these actions are performed once, before statement is executed. This makes with statements efficient as well as concise.
Declarations and statements The syntax of an if...then...else statement is if expression then statement1 else statement2 where expression returns a Boolean value. If expression is True, then statement1 is executed; otherwise statement2 is executed. For example, if J = 0 then Exit else Result := I/J; The then and else clauses contain one statement each, but it can be a structured statement.
Declarations and statements is equivalent to if ... { expression1 } then begin if ... { expression2 } then ... { statement1 } else ... { statement2 } end; The rule is that nested conditionals are parsed starting from the innermost conditional, with each else bound to the nearest available if on its left. To force the compiler to read our example in the second way, you would have to write it explicitly as if ... { expression1 } then begin if ... { expression2 } then ... { statement1 } end else ...
Declarations and statements Each value represented by a caseList must be unique in the case statement; subranges and lists cannot overlap. A case statement can have a final else clause: case selectorExpression of caseList1: statement1; ƒ caseListn: statementn; else statements; end where statements is a semicolon-delimited sequence of statements. When a case statement is executed, at most one of statement1 ... statementn is executed.
Declarations and statements Control loops Loops allow you to execute a sequence of statements repeatedly, using a control condition or variable to determine when the execution stops. Delphi has three kinds of control loop: repeat statements, while statements, and for statements. You can use the standard Break and Continue procedures to control the flow of a repeat, while, or for statement. Break terminates the statement in which it occurs, while Continue begins executing the next iteration of the sequence.
Declarations and statements Examples of while statements include while Data[I] <> X do I := I + 1; while I > 0 do begin if Odd(I) then Z := Z * X; I := I div 2; X := Sqr(X); end; while not Eof(InputFile) do begin Readln(InputFile, Line); Process(Line); end; For statements A for statement, unlike a repeat or while statement, requires you to specify explicitly the number of iterations you want the loop to go through.
Blocks and scope For purposes of controlling execution of the loop, the expressions initialValue and finalValue are evaluated only once, before the loop begins. Hence the for...to statement is almost, but not quite, equivalent to this while construction: begin counter := initialValue; while counter <= finalValue do begin statement; counter := Succ(counter); end; end The difference between this construction and the for...to statement is that the while loop reevaluates finalValue before each iteration.
Blocks A block consists of a series of declarations followed by a compound statement. All declarations must occur together at the beginning of the block. So the form of a block is declarations begin statements end The declarations section can include, in any order, declarations for variables, constants (including resource strings), types, procedures, functions, and labels. In a program block, the declarations section can also include one or more exports clauses (see Chapter 9, “Libraries and packages”).
The rules that determine identifier scope are summarized below. If the identifier is declared in ... its scope extends ... the declaration section of a program, function, or procedure from the point where it is declared to the end of the current block, including all blocks enclosed within that scope. the interface section of a unit from the point where it is declared to the end of the unit, and to any other unit or program that uses that unit. (See Chapter 3, “Programs and units”.
The System and SysInit units are used automatically by every program or unit. The declarations in System, along with the predefined types, routines, and constants that the compiler understands automatically, always have the outermost scope. You can override these rules of scope and bypass an inner declaration by using a qualified identifier (see “Qualified identifiers” on page 4-3) or a with statement (see “With statements” on page 4-22).
Chapter 5 Data types, variables, and constants Chapter5 A type is essentially a name for a kind of data. When you declare a variable you must specify its type, which determines the set of values the variable can hold and the operations that can be performed on it. Every expression returns data of a particular type, as does every function. Most functions and procedures require parameters of specific types.
About types portability. However, changes in storage format from one implementation of a generic type to the next could cause compatibility problems—for example, if you are streaming content to a file as raw, binary data, without type and versioning information. • Types can be classified as simple, string, structured, pointer, procedural, or variant.
Simple types Simple types Simple types, which include ordinal types and real types, define ordered sets of values. Ordinal types Ordinal types include integer, character, Boolean, enumerated, and subrange types. An ordinal type defines an ordered set of values in which each value except the first has a unique predecessor and each value except the last has a unique successor. Further, each value has an ordinality which determines the ordering of the type.
Simple types Integer types An integer type represents a subset of the whole numbers. The generic integer types are Integer and Cardinal; use these whenever possible, since they result in the best performance for the underlying CPU and operating system. The table below gives their ranges and storage formats for the current 32-bit Delphi compiler. Table 5.1 Generic integer types for 32-bit implementations of Delphi Type Range Format Integer –2147483648..2147483647 signed 32-bit Cardinal 0..
Simple types When you increment the last value or decrement the first value of an integer type, the result wraps around the beginning or end of the range. For example, the Shortint type has the range –128..127; hence, after execution of the code var I: Shortint; ƒ I := High(Shortint); I := I + 1; the value of I is –128. If compiler range-checking is enabled, however, this code generates a runtime error. Character types The fundamental character types are AnsiChar and WideChar.
Simple types For more information about Unicode characters, see “About extended character sets” on page 5-13 and “Working with null-terminated strings” on page 5-14. Boolean types The four predefined Boolean types are Boolean, ByteBool, WordBool, and LongBool. Boolean is the preferred type. The others exist to provide compatibility with other languages and operating system libraries.
Simple types defines an enumerated type called Suit whose possible values are Club, Diamond, Heart, and Spade, where Ord(Club) returns 0, Ord(Diamond) returns 1, and so forth. When you declare an enumerated type, you are declaring each val to be a constant of type typeName. If the val identifiers are used for another purpose within the same scope, naming conflicts occur.
Simple types Enumerated types with explicitly assigned ordinality By default, the ordinalities of enumerated values start from 0 and follow the sequence in which their identifiers are listed in the type declaration. You can override this by explicitly assigning ordinalities to some or all of the values in the declaration. To assign an ordinality to a value, follow its identifier with = constantExpression, where constantExpression is a constant expression that evaluates to an integer.
Simple types You can use numeric constants and characters (string constants of length 1) to define subrange types: type SomeNumbers = -128..127; Caps = 'A'..'Z'; When you use numeric or character constants to define a subrange, the base type is the smallest integer or character type that contains the specified range. The LowerBound..UpperBound construction itself functions as a type name, so you can use it directly in variable declarations. For example, var SomeNum: 1..
Simple types Real types A real type defines a set of numbers that can be represented with floating-point notation. The table below gives the ranges and storage formats for the fundamental real types. Table 5.3 Type Fundamental real types Range 10–39 Significant digits .. 1.7 x 1038 Real48 2.9 x Single 1.5 x 10–45 .. 3.4 x 1038 Double Size in bytes 11–12 6 7–8 4 5.0 x 10–324 .. 1.7 x 10308 15–16 8 Extended 3.6 x 10–4951 .. 1.1 x 104932 19–20 10 Comp –263+1 ..
String types String types A string represents a sequence of characters. Delphi supports the following predefined string types. Table 5.
String types Be careful indexing strings in this way, since overwriting the end of a string can cause access violations. Also, avoid passing long-string indexes as var parameters, because this results in inefficient code. You can assign the value of a string constant—or any other expression that returns a string—to a variable. The length of the string changes dynamically when the assignment is made.
String types Long strings AnsiString, also called a long string, represents a dynamically allocated string whose maximum length is limited only by available memory. A long-string variable is a pointer occupying four bytes of memory. When the variable is empty—that is, when it contains a zero-length string—the pointer is nil and the string uses no additional storage. When the variable is nonempty, it points a dynamically allocated block of memory that contains the string value.
String types system supports Unicode (UCS-2). The Linux operating system supports UCS-4, a superset of UCS-2. Borland’s RAD products support UCS-2 on both platforms. The Delphi language supports single-byte and multibyte characters and strings through the Char, PChar, AnsiChar, PAnsiChar, and AnsiString types. Indexing of multibyte strings is not reliable, since S[i] represents the ith byte (not necessarily the ith character) in S.
String types points P to an area of memory that contains a null-terminated copy of “Hello world!” This is equivalent to const TempString: array[0..12] of Char = 'Hello world!'#0; var P: PChar; ƒ P := @TempString[0]; You can also pass string constants to any function that takes value or const parameters of type PChar or PWideChar—for example StrUpper('Hello world!'). As with assignments to a PChar, the compiler generates a null-terminated copy of the string and gives the function a pointer to that copy.
String types The StrUpper function illustrates the use of pointer indexing to iterate through a nullterminated string: function StrUpper(Dest, Source: PChar; MaxLen: Integer): PChar; var I: Integer; begin I := 0; while (I < MaxLen) and (Source[I] <> #0) do begin Dest[I] := UpCase(Source[I]); Inc(I); end; Dest[I] := #0; Result := Dest; end; Mixing Delphi strings and null-terminated strings You can mix long strings (AnsiString values) and null-terminated strings (PChar values) in expressions and assignments
Structured types • PChar(S) always returns a pointer to a memory block; if S is empty, a pointer to #0 is returned. • When you cast a long-string variable to a pointer, the pointer remains valid until the variable is assigned a new value or goes out of scope. If you cast any other long-string expression to a pointer, the pointer is valid only within the statement where the typecast is performed. • When you cast a long-string expression to a pointer, the pointer should usually be considered read-only.
Structured types Sets A set is a collection of values of the same ordinal type. The values have no inherent order, nor is it meaningful for a value to be included twice in a set. The range of a set type is the power set of a specific ordinal type, called the base type; that is, the possible values of the set type are all the subsets of the base type, including the empty set. The base type can have no more than 256 possible values, and their ordinalities must fall between 0 and 255.
Structured types Arrays An array represents an indexed collection of elements of the same type (called the base type). Because each element has a unique index, arrays, unlike sets, can meaningfully contain the same value more than once. Arrays can be allocated statically or dynamically. Static arrays Static array types are denoted by constructions of the form array[indexType1, ..., indexTypen] of baseType where each indexType is an ordinal type whose range does not exceed 2GB.
Structured types Dynamic arrays Dynamic arrays do not have a fixed size or length. Instead, memory for a dynamic array is reallocated when you assign a value to the array or pass it to the SetLength procedure. Dynamic-array types are denoted by constructions of the form array of baseType For example, var MyFlexibleArray: array of Real; declares a one-dimensional dynamic array of reals. The declaration does not allocate memory for MyFlexibleArray. To create the array in memory, call SetLength.
Structured types In contrast, to make an independent copy of a dynamic array, you must use the global Copy function: var A, B: array of Integer; begin SetLength(A, 1); A[0] := 1; B := Copy(A); B[0] := 2; { B[0] <> A[0] } end; When dynamic-array variables are compared, their references are compared, not their array values. Thus, after execution of the code var A, B: array of Integer; begin SetLength(A, 1); SetLength(B, 1); A[0] := 2; B[0] := 2; end; A = B returns False but A[0] = B[0] returns True.
Structured types Multidimensional dynamic arrays To declare multidimensional dynamic arrays, use iterated array of ... constructions. For example, type TMessageGrid = array of array of string; var Msgs: TMessageGrid; declares a two-dimensional array of strings. To instantiate this array, call SetLength with two integer arguments. For example, if I and J are integer-valued variables, SetLength(Msgs,I,J); allocates an I-by-J array, and Msgs[0,0] denotes an element of that array.
Structured types To make the assignment work, declare the variables as var Int1, Int2: array[1..10] of Integer; or type IntArray = array[1..10] of Integer; var Int1: IntArray; Int2: IntArray; Records A record (analogous to a structure in some languages) represents a heterogeneous set of elements. Each element is called a field; the declaration of a record type specifies a name and type for each field.
Structured types Or use a with statement: with Record1 do begin Year := 1904; Month := Jun; Day := 16; end; You can now copy the values of Record1’s fields to Record2: Record2 := Record1; Because the scope of a field designator is limited to the record in which it occurs, you don’t have to worry about naming conflicts between field designators and other variables. Instead of defining record types, you can use the record ...
Structured types • Each constantList is a constant denoting a value of type ordinalType, or a commadelimited list of such constants. No value can be represented more than once in the combined constantLists. • Each variant is a semicolon-delimited list of declarations resembling the fieldList: type constructions in the main part of the record type.
Structured types type TShapeList = (Rectangle, Triangle, Circle, Ellipse, Other); TFigure = record case TShapeList of Rectangle: (Height, Width: Real); Triangle: (Side1, Side2, Angle: Real); Circle: (Radius: Real); Ellipse, Other: (); end; For each record instance, the compiler allocates enough memory to hold all the fields in the largest variant.
Pointers and pointer types You can also use the file of ... construction directly in a variable declaration. For example, var List1: file of PhoneEntry; The word file by itself indicates an untyped file: var DataFile: file; For more information, see “Untyped files” on page 8-4. Files are not allowed in arrays or records. Pointers and pointer types A pointer is a variable that denotes a memory address.
Pointers and pointer types The symbol ^ has two purposes, both of which are illustrated in our example. When it appears before a type identifier— ^typeName —it denotes a type that represents pointers to variables of type typeName. When it appears after a pointer variable— pointer^ —it dereferences the pointer; that is, it returns the value stored at the memory address held by the pointer.
Pointers and pointer types Pointer types You can declare a pointer to any type, using the syntax type pointerTypeName = ^type When you define a record or other data type, it’s a common practice also to define a pointer to that type. This makes it easy to manipulate instances of the type without copying large blocks of memory. Standard pointer types exist for many purposes. The most versatile is Pointer, which can point to data of any kind.
Procedural types Table 5.6 Selected pointer types declared in System and SysUtils (continued) Pointer type Points to variables of type PShortString ShortString. Useful when porting legacy code that uses the old PString type. PTextBuf TTextBuf (declared in SysUtils). TTextBuf is the internal buffer type in a TTextRec file record.) PVarRec TVarRec (declared in System) PVariant Variant PWideString WideString PWordArray TWordArray (declared in SysUtils).
Procedural types The previous variables are all procedure pointers—that is, pointers to the address of a procedure or function. If you want to reference a method of an instance object (see Chapter 7, “Classes and objects”), you need to add the words of object to the procedural type name. For example type TMethod = procedure of object; TNotifyEvent = procedure(Sender: TObject) of object; These types represent method pointers.
Procedural types Procedural types in statements and expressions When a procedural variable is on the left side of an assignment statement, the compiler expects a procedural value on the right. The assignment makes the variable on the left a pointer to the function or procedure indicated on the right. In other contexts, however, using a procedural variable results in a call to the referenced procedure or function.
Variant types The @ operator can also be used to assign an untyped pointer value to a procedural variable. For example, var StrComp: function(Str1, Str2: PChar): Integer; ƒ @StrComp := GetProcAddress(KernelHandle, 'lstrcmpi'); calls the GetProcAddress function and points StrComp to the result. Any procedural variable can hold the value nil, which means that it points to nothing. But attempting to call a nil-valued procedural variable is an error.
Variant types The standard function VarType returns a variant’s type code. The varTypeMask constant is a bit mask used to extract the code from VarType’s return value, so that, for example, VarType(V) and varTypeMask = varDouble returns True if V contains a Double or an array of Double. (The mask simply hides the first bit, which indicates whether the variant holds an array.
Variant types Table 5.
Variant types In this example, the Else part of the If statement will be executed. This behavior can be changed by setting the NullEqualityRule and NullMagnitudeRule global variables. Refer to the CLX online documentation for more information. Variant arrays You cannot assign an ordinary static array to a variant. Instead, create a variant array by calling either of the standard functions VarArrayCreate or VarArrayOf.
Type compatibility and identity When you assign a Variant that contains custom data (such as a Delphi string, or a one of the new custom variant types) to an OleVariant, the runtime library tries to convert the Variant into one of the OleVariant standard data types (such as a Delphi string converts to an OLE BSTR string). For example, if a variant containing an AnsiString is assigned to an OleVariant, the AnsiString becomes a WideString.
Type compatibility and identity Type compatibility Every type is compatible with itself. Two distinct types are compatible if they satisfy at least one of the following conditions. • They are both real types. • They are both integer types. • One type is a subrange of the other. • Both types are subranges of the same type. • Both are set types with compatible base types. • Both are packed-string types with the same number of characters.
Declaring types • T1 and T2 are compatible packed-string types. • T1 and T2 are compatible set types. • T1 and T2 are compatible pointer types. • T1 and T2 are both class, class-reference, or interface types and T2 is a derived from T1. • T1 is an interface type and T2 is a class type that implements T1. • T1 is PChar or PWideChar and T2 is a zero-based character array of the form array[0..n] of Char (when T1 is PChar) or of WideChar (when T1 is PWideChar). • T1 and T2 are compatible procedural types.
Variables X and Y are of the same type; at runtime, there is no way to distinguish TValue from Real. This is usually of little consequence, but if your purpose in defining a new type is to utilize runtime type information—for example, to associate a property editor with properties of a particular type—the distinction between “different name” and “different type” becomes important.
Variables Consecutive variable declarations do not have to repeat the reserved word var: var X, Y, Z: Double; I, J, K: Integer; Digit: 0..9; Okay: Boolean; Variables declared within a procedure or function are sometimes called local, while other variables are called global. Global variables can be initialized at the same time they are declared, using the syntax var identifier: type = constantExpression; where constantExpression is any constant expression representing a value of type type.
Declared constants Dynamic variables You can create dynamic variables by calling the GetMem or New procedure. Such variables are allocated on the heap and are not managed automatically. Once you create one, it is your responsibility ultimately to free the variable’s memory; use FreeMem to destroy variables created by GetMem and Dispose to destroy variables created by New. Other standard routines that operate on dynamic variables include ReallocMem, AllocMem, Initialize, Finalize, StrAlloc, and StrDispose.
Declared constants Declared constants are either true constants or typed constants. These two kinds of constant are superficially similar, but they are governed by different rules and used for different purposes. True constants A true constant is a declared identifier whose value cannot change. For example, const MaxValue = 237; declares a constant called MaxValue that returns the integer 237.
Declared constants Here are some examples of 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'..
Resource strings Resource strings are stored as resources and linked into the executable or library so that they can be modified without recompiling the program. For more information, see the online Help topics on localizing applications. Resource strings are declared like other true constants, except that the word const is replaced by resourcestring. The expression to the right of the = symbol must be a constant expression and must return a string value.
creates an array called Maze where Maze[0,0,0] = 0 Maze[0,0,1] = 1 Maze[0,1,0] = 2 Maze[0,1,1] = 3 Maze[1,0,0] = 4 Maze[1,0,1] = 5 Maze[1,1,0] = 6 Maze[1,1,1] = 7 Array constants cannot contain file-type values at any level. Record constants To declare a record constant, specify the value of each field—as fieldName: value, with the field assignments separated by semicolons—in parentheses at the end of the declaration. The values must be represented by constant expressions.
Given these declarations, you can use the procedural constant MyFunction in a function call: I := MyFunction(5, 7) You can also assign the value nil to a procedural constant. Pointer constants When you declare a pointer constant, you must initialize it to a value that can be resolved—at least as a relative address—at compile time. There are three ways to do this: with the @ operator, with nil, and (if the constant is of type PChar or PWideChar) with a string literal.
5-48 Delphi Language Guide
Chapter 6 Procedures and functions Chapter6 Procedures and functions, referred to collectively as routines, are self-contained statement blocks that can be called from different locations in a program. A function is a routine that returns a value when it executes. A procedure is a routine that does not return a value. Function calls, because they return a value, can be used as expressions in assignments and operations. For example, I := SomeFunction(X); calls SomeFunction and assigns the result to I.
Declaring procedures and functions Declaring procedures and functions When you declare a procedure or function, you specify its name, the number and type of parameters it takes, and, in the case of a function, the type of its return value; this part of the declaration is sometimes called the prototype, heading, or header. Then you write a block of code that executes whenever the procedure or function is called; this part is sometimes called the routine’s body or block.
Declaring procedures and functions This procedure call assigns the value “17” to MyString (which must be a string variable). Within a procedure’s statement block, you can use variables and other identifiers declared in the localDeclarations part of the procedure. You can also use the parameter names from the parameter list (like N and S in the previous example); the parameter list defines a set of local variables, so don’t try to redeclare the parameter names in the localDeclarations section.
Declaring procedures and functions defines a constant function called WF that takes no parameters and always returns the integer value 17.
Declaring procedures and functions If the function exits without assigning a value to Result or the function name, then the function’s return value is undefined. Calling conventions When you declare a procedure or function, you can specify a calling convention using one of the directives register, pascal, cdecl, stdcall, and safecall. For example, function MyFunction(X, Y: Real): Real; cdecl; ƒ Calling conventions determine the order in which parameters are passed to the routine.
Declaring procedures and functions The directives near, far, and export refer to calling conventions in 16-bit Windows programming. They have no effect in 32-bit applications and are maintained for backward compatibility only. Forward and interface declarations The forward directive replaces the block, including local variable declarations and statements, in a procedure or function declaration. For example, function Calculate(X, Y: Integer): Real; forward; declares a function called Calculate.
Declaring procedures and functions When importing a C function that takes a variable number of parameters, use the varargs directive. For example, function printf(Format: PChar): Integer; cdecl; varargs; The varargs directive works only with external routines and only with the cdecl calling convention. Linking to object files To call routines from a separately compiled object file, first link the object file to your application using the $L (or $LINK) compiler directive.
Declaring procedures and functions where the first stringConstant gives the name of the library file and the second stringConstant is the routine’s original name. On Windows: The following declaration imports a function from user32.dll (part of the Windows API). function MessageBox(HWnd: Integer; Text, Caption: PChar; Flags: Integer): Integer; stdcall; external 'user32.dll' name 'MessageBoxA'; The function’s original name is MessageBoxA, but it is imported as MessageBox.
Declaring procedures and functions You can pass to an overloaded routine parameters that are not identical in type with those in any of the routine’s declarations, but that are assignment-compatible with the parameters in more than one declaration.
Declaring procedures and functions Variants can also be used as parameters in overloaded function declarations. Variant is considered more general than any simple type. Preference is always given to exact type matches over variant matches. If a variant is passed into such an overload situation, and an overload that takes a variant exists in that parameter position, it is considered to be an exact match for the Variant type. This can cause some minor side effects with float types.
Parameters in Unit1; if no routine in Unit1 matches the name and parameter list in the call, an error results. For information about distributing overloaded methods in a class hierarchy, see “Overloading methods” on page 7-12. For information about exporting overloaded routines from a shared library, see “The exports clause” on page 9-6. Local declarations The body of a function or procedure often begins with declarations of local variables used in the routine’s statement block.
Parameters the = symbol and a default value. Parameter names must be valid identifiers. Any declaration can be preceded by var, const, or out. Examples: (X, Y: Real) (var S: string; X: Integer) (HWnd: Integer; Text, Caption: PChar; Flags: Integer) (const P; I: Integer) The parameter list specifies the number, order, and type of parameters that must be passed to the routine when it is called.
Parameters These functions return the same result, but only the second one—DoubleByRef—can change the value of a variable passed to it. Suppose we call the functions like this: var I, J, V, W: Integer; begin I := 4; V := 4; J := DoubleByValue(I); W := DoubleByRef(V); end; // J = 8, I = 4 // W = 8, V = 8 After this code executes, the variable I, which was passed to DoubleByValue, has the same value we initially assigned to it. But the variable V, which was passed to DoubleByRef, has a different value.
Parameters as a var parameter to another routine. (But when you pass an object reference as a constant parameter, you can still modify the object’s properties.) Using const allows the compiler to optimize code for structured- and string-type parameters. It also provides a safeguard against unintentionally passing a parameter by reference to another routine.
Parameters The following example uses untyped parameters in a function called Equal that compares a specified number of bytes of any two variables. function Equal(var Source, Dest; Size: Integer): Boolean; type TBytes = array[0..MaxInt - 1] of Byte; var N: Integer; begin N := 0; while (N < Size) and (TBytes(Dest)[N] = TBytes(Source)[N]) do Inc(N); Equal := N = Size; end; Given the declarations type TVector = array[1..
Parameters Array parameters When you declare routines that take array parameters, you cannot include index type specifiers in the parameter declarations. That is, the declaration procedure Sort(A: array[1..10] of Integer); // syntax error causes a compilation error. But type TDigits = array[1..10] of Integer; procedure Sort(A: TDigits); is valid. For most purposes, however, open array parameters are a better solution.
Parameters Note The syntax of open array parameters resembles that of dynamic array types, but they do not mean the same thing. The previous example creates a function that takes any array of Char elements, including (but not limited to) dynamic arrays. To declare parameters that must be dynamic arrays, you need to specify a type identifier: type TDynamicCharArray = array of Char; function Find(A: TDynamicCharArray): Integer; For information about dynamic arrays, see “Dynamic arrays” on page 5-20.
Parameters Variant open array parameters Variant open array parameters allow you to pass an array of differently typed expressions to a single procedure or function. To define a routine with a variant open array parameter, specify array of const as the parameter’s type. Thus procedure DoSomething(A: array of const); declares a procedure called DoSomething that can operate on heterogeneous arrays. The array of const construction is equivalent to array of TVarRec.
Parameters Default parameters You can specify default parameter values in a procedure or function heading. Default values are allowed only for typed const and value parameters. To provide a default value, end the parameter declaration with the = symbol followed by a constant expression that is assignment-compatible with the parameter’s type. For example, given the declaration procedure FillArray(A: array of Integer; Value: Integer = 0); the following procedure calls are equivalent.
Default parameters and overloaded routines If you use default parameter values in an overloaded routine, avoid ambiguous parameter signatures. Consider, for example, the following. procedure Confused(I: Integer); overload; ƒ procedure Confused(I: Integer; J: Integer = 0); overload; ƒ Confused(X); // Which procedure is called? In fact, neither procedure is called. This code generates a compilation error.
You can omit parentheses when passing all and only the default parameters to a routine. For example, given the procedure procedure DoSomething(X: Real = 1.0; I: Integer = 0; S: string = ''); the following calls are equivalent. DoSomething(); DoSomething; Open array constructors Open array constructors allow you to construct arrays directly within function and procedure calls. They can be passed only as open array parameters or variant open array parameters.
6-22 Delphi Language Guide
Chapter 7 Classes and objects Chapter7 A class, or class type, defines a structure consisting of fields, methods, and properties. Instances of a class type are called objects. The fields, methods, and properties of a class are called its components or members. • A field is essentially a variable that is part of an object. Like the fields of a record, a class’s fields represent data items that exist in each instance of the class. • A method is a procedure or function associated with a class.
Class types Class types A class type must be declared and given a name before it can be instantiated. (You cannot define a class type within a variable declaration.) Declare classes only in the outermost scope of a program or unit, not in a procedure or function declaration.
Class types Inheritance and scope When you declare a class, you can specify its immediate ancestor. For example, type TSomeControl = class(TControl); declares a class called TSomeControl that descends from TControl. A class type automatically inherits all of the members from its immediate ancestor. Each class can declare new members and can redefine inherited ones, but a class cannot remove members defined in an ancestor.
Class types Object types As an alternative to class types, you can declare object types using the syntax type objectTypeName = object (ancestorObjectType) memberList end; where objectTypeName is any valid identifier, (ancestorObjectType) is optional, and memberList declares fields, methods, and properties. If (ancestorObjectType) is omitted, then the new type has no ancestor. Object types cannot have published members.
Class types You can increase the visibility of a member in a descendant class by redeclaring it, but you cannot decrease its visibility. For example, a protected property can be made public in a descendant, but not private. Moreover, published members cannot become public in a descendant class. For more information, see “Property overrides and redeclarations” on page 7-23. Private, protected, and public members A private member is invisible outside of the unit or program where its class is declared.
Class types A class cannot have published members unless it is compiled in the {$M+} state or descends from a class compiled in the {$M+} state. Most classes with published members derive from TPersistent, which is compiled in the {$M+} state, so it is seldom necessary to use the $M directive. Automated members Automated members have the same visibility as public members. The difference is that Automation type information (required for Automation servers) is generated for automated members.
Fields Forward declarations allow mutually dependent classes. For example, type TFigure = class; // forward declaration TDrawing = class Figure: TFigure; ƒ end; TFigure = class // defining declaration Drawing: TDrawing; ƒ end; Do not confuse forward declarations with complete declarations of types that derive from TObject without declaring any class members.
Methods Although MyObject holds an instance of TDescendant, it is declared as TAncestor. The compiler therefore interprets MyObject.Value as referring to the (integer) field declared in TAncestor. Both fields, however, exist in the TDescendant object; the inherited Value is hidden by the new one, and can be accessed through a typecast. Methods A method is a procedure or function associated with a class.
Methods Method declarations can include special directives that are not used with other functions or procedures. Directives should appear in the class declaration only, not in the defining declaration, and should always be listed in the following order: reintroduce; overload; binding; calling convention; abstract; warning where binding is virtual, dynamic, or override; calling convention is register, pascal, cdecl, stdcall, or safecall; and warning is platform, deprecated, or library.
Methods Self is useful for a variety of reasons. For example, a member identifier declared in a class type might be redeclared in the block of one of the class’s methods. In this case, you can access the original member identifier as Self.Identifier. For information about Self in class methods, see “Class methods” on page 7-26. Method binding Method bindings can be static (the default), virtual, or dynamic. Virtual and dynamic methods can be overridden, and they can be abstract.
Methods Virtual and dynamic methods To make a method virtual or dynamic, include the virtual or dynamic directive in its declaration. Virtual and dynamic methods, unlike static methods, can be overridden in descendant classes. When an overridden method is called, the actual (runtime) type of the class or object used in the method call—not the declared type of the variable— determines which implementation to activate. To override a method, redeclare it with the override directive.
Methods Overriding versus hiding If a method declaration specifies the same method identifier and parameter signature as an inherited method, but doesn’t include override, the new declaration merely hides the inherited one without overriding it. Both methods exist in the descendant class, where the method name is statically bound.
Methods If you overload a virtual method, use the reintroduce directive when you redeclare it in descendant classes. For example, type T1 = class(TObject) procedure Test(I: Integer); overload; virtual; end; T2 = class(T1) procedure Test(S: string); reintroduce; overload; end; ƒ SomeObject := T2.Create; SomeObject.Test('Hello!'); // calls T2.Test SomeObject.Test(7); // calls T1.Test Within a class, you cannot publish multiple overloaded methods with the same name.
Methods parameters to the constructor. Finally, the constructor returns a reference to the newly allocated and initialized object. The type of the returned value is the same as the class type specified in the constructor call. If an exception is raised during execution of a constructor that was invoked on a class reference, the Destroy destructor is automatically called to destroy the unfinished object.
Methods Destructors A destructor is a special method that destroys the object where it is called and deallocates its memory. The declaration of a destructor looks like a procedure declaration, but it begins with the word destructor. Example: destructor SpecialDestructor(SaveData: Boolean); destructor Destroy; override; Destructors must use the default register calling convention.
Methods the Messages unit. A message method must be a procedure that takes a single var parameter.
Properties for the given ID, inherited calls the DefaultHandler method originally defined in TObject. The implementation of DefaultHandler in TObject simply returns without performing any actions. By overriding DefaultHandler, a class can implement its own default handling of messages. On Windows, the DefaultHandler method for WinCLX controls calls the Windows DefWindowProc function. Message dispatching Message handlers are seldom called directly.
Properties Properties are defined by their access specifiers. Unlike fields, properties cannot be passed as var parameters, nor can the @ operator be applied to a property. The reason is that a property doesn’t necessarily exist in memory. It could, for instance, have a read method that retrieves a value from a database or generates a random value. Property access Every property has a read specifier, a write specifier, or both.
Properties When a property is referenced in an expression, its value is read using the field or method listed in the read specifier. When a property is referenced in an assignment statement, its value is written using the field or method listed in the write specifier. The example below declares a class called TCompass with a published property called Heading. The value of Heading is read through the FHeading field and written through the SetHeading procedure. type THeading = 0..
Properties Array properties Array properties are indexed properties. They can represent things like items in a list, child controls of a control, and pixels of a bitmap. The declaration of an array property includes a parameter list that specifies the names and types of the indexes.
Properties The definition of an array property can be followed by the default directive, in which case the array property becomes the default property of the class. For example, type TStringArray = class public property Strings[Index: Integer]: string ...; default; ƒ end; If a class has a default property, you can access that property with the abbreviation object[index], which is equivalent to object.property[index]. For example, given the declaration above, StringArray.
Properties Given the declaration above, if Rectangle is of type TRectangle, then Rectangle.Right := Rectangle.Left + 100; corresponds to Rectangle.SetCoordinate(2, Rectangle.GetCoordinate(0) + 100); Storage specifiers The optional stored, default, and nodefault directives are called storage specifiers. They have no effect on program behavior, but control whether or not to save the values of published properties in form files.
Properties Property overrides and redeclarations A property declaration that doesn’t specify a type is called a property override. Property overrides allow you to change a property’s inherited visibility or specifiers. The simplest override consists only of the reserved word property followed by an inherited property identifier; this form is used to change a property’s visibility.
Class references can cast MyObject to TDescendant to access the descendant class’s properties and their access specifiers. type TAncestor = class ƒ property Value: Integer read Method1 write Method2; end; TDescendant = class(TAncestor) ƒ property Value: Integer read Method3 write Method4; end; var MyObject: TAncestor; ƒ MyObject := TDescendant.Create; Class references Sometimes operations are performed on a class itself, rather than on instances of a class (that is, objects).
Class references This declaration says that to create a TCollection instance object, you must pass to the constructor the name of a class descending from TCollectionItem. Class-reference types are useful when you want to invoke a class method or virtual constructor on a class or object whose actual type is unknown at compile time. Constructors and class references A constructor can be called using a variable of a class-reference type.
Class references The is operator The is operator, which performs dynamic type checking, is used to verify the actual runtime class of an object. The expression object is class returns True if object is an instance of the class denoted by class or one of its descendants, and False otherwise. (If object is nil, the result is False.) If the declared type of object is unrelated to class—that is, if the types are distinct and one is not an ancestor of the other—a compilation error results.
Exceptions The defining declaration of a class method must also begin with 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 where the method is called (which could be a descendant of the class in which it is defined). If the method is called in the class C, then Self is of the type class of C.
Exceptions Conditional statements are often the best way to test for errors. For example, suppose you want to make sure that a file exists before trying to open it. You could do it this way: try AssignFile(F, FileName); Reset(F); // raises an EInOutError exception if file is not found except on Exception do ...
Exceptions Raising and handling exceptions To raise an exception object, use an instance of the exception class with a raise statement. For example, raise EMathError.Create; In general, the form of a raise statement is raise object at address where object and at address are both optional; see “Re-raising exceptions” on page 7-32. When an address is specified, it can be any expression that evaluates to a pointer type, but is usually a pointer to a procedure or function. For example: raise Exception.
Exceptions Try...except statements Exceptions are handled within try...except statements. For example, try X := Y/Z; except on EZeroDivide do HandleZeroDivide; end; This statement attempts to divide Y by Z, but calls a routine named HandleZeroDivide if an EZeroDivide exception is raised. The syntax of a try...
Exceptions When an exception is handled, the stack is traced back to the procedure or function containing the try...except statement where the handling occurs, and control is transferred to the executed exception handler, else clause, or statement list. This process discards all procedure and function calls that occurred after entering the try...except statement where the exception is handled.
Exceptions An exception block that contains no exception handlers, but instead consists only of a list of statements, handles all exceptions. For example, try ƒ except HandleException; end; Here, the HandleException routine handles any exception that occurs as a result of executing the statements between try and except. Re-raising exceptions When the reserved word raise occurs in an exception block without an object reference following it, it raises whatever exception is handled by the block.
Nested exceptions Code executed in an exception handler can itself raise and handle exceptions. As long as these exceptions 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 below.
where each statementList is a sequence of statements delimited by semicolons. The try...finally statement executes the statements in statementList1 (the try clause). If statementList1 finishes without raising exceptions, statementList2 (the finally clause) is executed. If an exception is raised during execution of statementList1, control is transferred to statementList2; once statementList2 finishes executing, the exception is re-raised.
Chapter 8 Standard routines and I/O Chapter8 This chapter discusses text and file I/O and summarizes standard library routines. Many of the procedures and functions listed here are defined in the System and SysInit units, which are implicitly used with every application. Others are built into the compiler but are treated as if they were in the System unit. Some standard routines are in units such as SysUtils, which must be listed in a uses clause to make them available in programs.
File input and output Table 8.1 Input and output procedures and functions (continued) Procedure or function Description Flush Flushes the buffer of an output text file. GetDir Returns the current directory of a specified drive. IOResult Returns an integer value that is the status of the last I/O function performed. MkDir Creates a subdirectory. Read Reads one or more values from a file into one or more variables.
File input and output When a program completes processing a file, the file must be closed using the standard procedure CloseFile. After a file is closed, its associated external file is updated. The file variable can then be associated with another external file. By default, all calls to standard I/O procedures and functions are automatically checked for errors, and if an error occurs an exception is raised (or the program is terminated if exception handling is not enabled).
Text file device drivers Some of the standard I/O routines that work on text files don’t need to have a file variable explicitly given as a parameter. If the file parameter is omitted, Input or Output is assumed by default, depending on whether the procedure or function is input- or output-oriented. For example, Read(X) corresponds to Read(Input, X) and Write(X) corresponds to Write(Output, X).
Text file device drivers To associate the device-interface functions with a specific file, you must write a customized Assign procedure. The Assign procedure must assign the addresses of the four device-interface functions to the four function pointers in the text file variable. In addition, it should store the fmClosed “magic” constant in the Mode field, store the size of the text file buffer in BufSize, store a pointer to the text file buffer in BufPtr, and clear the Name string.
Handling null-terminated strings The InOut function The InOut function is called by the Read, Readln, Write, Writeln, Eof, Eoln, SeekEof, SeekEoln, and CloseFile standard routines whenever input or output from the device is required. When Mode is fmInput, the InOut function reads up to BufSize characters into BufPtr^, and returns the number of characters read in BufEnd. In addition, it stores zero in BufPos.
Handling null-terminated strings Table 8.2 Null-terminated string functions (continued) Function Description StrCopy Copies a string. StrDispose Disposes a character buffer allocated using StrAlloc or StrNew. StrECopy Copies a string and returns a pointer to the end of the string. StrEnd Returns a pointer to the end of a string. StrFmt Formats one or more values into a string. StrIComp Compares two strings without case sensitivity.
Other standard routines Other standard routines The table below lists frequently used procedures and functions found in Borland product libraries. This is not an exhaustive inventory of standard routines. For more information about these and other routines, see the online Help. Table 8.3 8-8 Other standard routines Procedure or function Description Addr Returns a pointer to a specified object. AllocMem Allocates a memory block and initializes each byte to zero.
Other standard routines Table 8.3 Other standard routines (continued) Procedure or function Description FormatFloat Formats a floating point value. FreeMem Releases allocated memory. GetMem Allocates dynamic memory and a pointer to the address of the block. Halt Initiates abnormal termination of a program. Hi Returns the high-order byte of an expression as an unsigned value. High Returns the highest value in the range of a type, array, or string.
Other standard routines Table 8.3 Other standard routines (continued) Procedure or function Description StrToCurr Converts a string to a currency value. StrToDate Converts a string to a date format (TDateTime). StrToDateTime Converts a string to a TDateTime. StrToFloat Converts a string to a floating-point value. StrToInt Converts a string to an integer. StrToTime Converts a string to a time format (TDateTime). StrUpper Returns an ASCII string in upper case.
Part II Special topics Part II The chapters in Part II cover specialized language features and advanced topics.
Chapter 9 Libraries and packages Chapter9 A dynamically loadable library is a dynamic-link library (DLL) on Windows or a shared object library file on Linux. It is a collection of routines that can be called by applications and by other DLLs or shared objects. Like units, dynamically loadable libraries contain sharable code or resources. But this type of library is a separately compiled executable that is linked at runtime to the programs that use it.
Calling dynamically loadable libraries Static loading The simplest way to import a procedure or function is to declare it using the external directive. For example, On Windows: procedure DoSomething; external 'MYLIB.DLL'; On Linux: procedure DoSomething; external 'mylib.so'; If you include this declaration in a program, MYLIB.DLL (Windows) or mylib.so (Linux) is loaded once, when the program starts.
Calling dynamically loadable libraries begin Handle := LoadLibrary('libraryname'); if Handle <> 0 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; When you import routines this way, the library is not loaded until the code containing the call to LoadLibrary executes. The library is later unloaded by the call to FreeLibrary.
Writing dynamically loadable libraries In this case, when importing routines, the shared object is not loaded until the code containing the call to dlopen executes. The shared object is later unloaded by the call to dlclose. This also allows you to conserve memory and to run your program even when some of the shared objects it uses are not present.
Writing dynamically loadable libraries Libraries can be built from multiple units. In this case, the library source file is frequently reduced to a uses clause, an exports clause, and the initialization code. For example, library Editors; uses EdInit, EdInOut, EdFormat, EdPrint; exports InitEditors, DoneEditors name Done, InsertText name Insert, DeleteSelection name Delete, FormatSelection, PrintSelection name Print, ƒ SetErrorHandler; begin InitLibrary; end.
Writing dynamically loadable libraries The exports clause A routine is exported when it is listed in an exports clause, which has the form exports entry1, ..., entryn; where each entry consists of the name of a procedure, function, or variable (which must be declared prior to the exports clause), followed by a parameter list (only if exporting a routine that is overloaded), and an optional name specifier. You can qualify the procedure or function name with the name of a unit.
Writing dynamically loadable libraries Library initialization code The statements in a library’s block constitute the library’s initialization code. These statements are executed once every time the library is loaded. They typically perform tasks like registering window classes and initializing variables. Library initialization code can also install an entry point procedure using the DllProc variable.
Writing dynamically loadable libraries Global variables in a library Global variables declared in a shared library cannot be imported by a Delphi application. A library can be used by several applications at once, but each application has a copy of the library in its own process space with its own set of global variables. For multiple libraries—or multiple instances of a library—to share memory, they must use memory-mapped files. Refer to the your system documentation for further information.
Writing dynamically loadable libraries Exceptions and runtime errors in libraries When an exception is raised but not handled in a dynamically loadable library, it propagates out of the library to the caller. If the calling application or library is itself written in Delphi, the exception can be handled through a normal try...except statement.
Packages Packages A package is a specially compiled library used by applications, the IDE, or both. Packages allow you to rearrange where code resides without affecting the source code. This is sometimes referred to as application partitioning. Runtime packages provide functionality when a user runs an application. Design-time packages are used to install components in the IDE and to create special property editors for custom components.
Packages where packageName is any valid identifier. The requiresClause and containsClause are both optional. For example, the following code declares the DATAX package. package DATAX; requires rtl, clx; contains Db, DBLocal, DBXpress, ... ; end. The requires clause lists other, external packages used by the package being declared. It consists of the directive requires, followed by a comma-delimited list of package names, followed by a semicolon.
Packages The requires clause The requires clause lists other, external packages that are used by the current package. It functions like the uses clause in a unit file. An external package listed in the requires clause is automatically linked at compile time into any application that uses both the current package and one of the units contained in the external package.
Compiling packages Packages are ordinarily compiled from the IDE using .dpk files generated by the Package editor. You can also compile .dpk files directly from the command line. When you build a project that contains a package, the package is implicitly recompiled, if necessary. Generated files The following table lists the files produced by the successful compilation of a package. Table 9.
Including {$DENYPACKAGEUNIT ON} in source code prevents the unit file from being packaged. Including {$G–} or {$IMPORTEDDATA OFF} may prevent a package from being used in the same application with other packages. Other compiler directives may be included, if appropriate, in package source code. Package-specific command-line compiler switches The following package-specific switches are available for the command-line compiler. See the online Help for details. Table 9.
Chapter 10 Object interfaces Chapter10 An object interface—or simply interface—defines methods that can be implemented by a class. Interfaces are declared like classes, but cannot be directly instantiated and do not have their own method definitions. Rather, it is the responsibility of any class that supports an interface to provide implementations for the interface’s methods.
Interface types • All members of an interface are public. Visibility specifiers and storage specifiers are not allowed. (But an array property can be declared as default.) • Interfaces have no constructors or destructors. They cannot be instantiated, except through classes that implement their methods. • Methods cannot be declared as virtual, dynamic, abstract, or override. Since interfaces do not implement their own methods, these designations have no meaning.
Interface types Interface identification An interface declaration can specify a globally unique identifier (GUID), represented by a string literal enclosed in brackets immediately preceding the member list. The GUID part of the declaration must have the form ['{xxxxxxxx–xxxx–xxxx–xxxx–xxxxxxxxxxxx}'] where each x is a hexadecimal digit (0 through 9 or A through F). On Windows, the Type Library editor automatically generates GUIDs for new interfaces.
Implementing interfaces Interface properties Properties declared in an interface are accessible only through expressions of the interface type; they cannot be accessed through class-type variables. Moreover, interface properties are visible only within programs where the interface is compiled. In an interface, property read and write specifiers must be methods, since fields are not available.
Implementing interfaces declares a class called TMemoryManager that implements the IMalloc and IErrorInfo interfaces. When a class implements an interface, it must implement (or inherit an implementation of) each method declared in the interface. Here is the declaration of TInterfacedObject in the System unit.
Implementing interfaces For example, the class declaration type TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo) function IMalloc.Alloc = Allocate; procedure IMalloc.Free = Deallocate; ƒ end; maps IMalloc’s Alloc and Free methods onto TMemoryManager’s Allocate and Deallocate methods. A method resolution clause cannot alter a mapping introduced by an ancestor class.
Implementing interfaces Implementing interfaces by delegation The implements directive allows you to delegate implementation of an interface to a property in the implementing class. For example, property MyInterface: IMyInterface read FMyInterface implements IMyInterface; declares a property called MyInterface that implements the interface IMyInterface. The implements directive must be the last specifier in the property declaration and can list more than one interface, separated by commas.
Implementing interfaces Delegating to a class-type property If the delegate property is of a class type, that class and its ancestors are searched for methods implementing the specified interface before the enclosing class and its ancestors are searched. Thus it is possible to implement some methods in the class specified by the property, and others in the class where the property is declared. Method resolution clauses can be used in the usual way to resolve ambiguities or specify a particular method.
Interface references Interface references If you declare a variable of an interface type, the variable can reference instances of any class that implements the interface. Such variables allow you to call interface methods without knowing at compile time where the interface is implemented. But they are subject to the following: • An interface-type expression gives you access only to methods and properties declared in the interface, not to other members of the implementing class.
Interface references Interface references are typically managed through reference-counting, which depends on the _AddRef and _Release methods inherited from IInterface. Using the default implementation of reference counting, when an object is referenced only through interfaces, there is no need to destroy it manually; the object is automatically destroyed when the last reference to it goes out of scope.
Automation objects (Windows only) An interface query returns nil if object is nil. Otherwise, it passes the GUID of interface to the QueryInterface method in object, raising an exception unless QueryInterface returns zero. If QueryInterface returns zero (indicating that object’s class implements interface), the interface query returns an interface reference to object.
Automation objects (Windows only) Dispatch interface properties Properties of a dispatch interface do not include access specifiers. They can be declared as read only or write only. To specify a dispatch ID for a property, include the dispid directive in its declaration, followed by an integer constant; specifying an already used ID causes an error. Array properties can be declared as default. No other directives are allowed in dispatch-interface property declarations.
Automation objects (Windows only) Some Automation servers allow you to omit parameters from a method call, accepting their default values. For example, Word.FileSaveAs('test.doc'); Word.FileSaveAs('test.doc', 6); Word.FileSaveAs('test.doc',,,'secret'); Word.FileSaveAs('test.doc', Password := 'secret'); Word.FileSaveAs(Password := 'secret', Name := 'test.doc'); Automation method call parameters can be of integer, real, string, Boolean, and variant types.
10-14 Delphi Language Guide
Chapter 11 Memory management Chapter11 This chapter explains how programs use memory and describes the internal formats of Delphi data types. The memory manager (Windows only) Note Linux uses glibc functions such as malloc for memory management. For information, refer to the malloc man page on your Linux system. On Windows systems, the memory manager manages all dynamic memory allocations and deallocations in an application.
The memory manager (Windows only) The memory manager maintains two status variables, AllocMemCount and AllocMemSize, which contain the number of currently allocated memory blocks and the combined size of all currently allocated memory blocks. Applications can use these variables to display status information for debugging. The System unit provides two procedures, GetMemoryManager and SetMemoryManager, that allow applications to intercept low-level memory manager calls.
Internal data formats Internal data formats The following sections describe the internal formats of Delphi data types. Integer types The format of 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..
Internal data formats Real types The real types store the binary representation of a sign (+ or –), an exponent, and a significand. A real value has the form +/– 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, the most significant bit is always on the left and the least significant bit on the right.
Internal data formats The Double type An 8-byte (64-bit) Double number is divided into three fields: 1 11 52 s e f The value v of the number is given by 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.
Internal data formats Long string types A long string variable occupies four bytes of memory which contain a pointer to a dynamically allocated string. When a long string variable is empty (contains a zerolength string), the string pointer is nil and no dynamic memory is associated with the string variable. For a nonempty string value, the string pointer points to a dynamically allocated block of memory that contains the string value in addition to a 32-bit length indicator and a 32-bit reference count.
Internal data formats The NULL character at the end of a wide string memory block is automatically maintained by the compiler and the built-in string handling routines. This makes it possible to typecast a wide string directly to a null-terminated string. Note On Linux, wide strings are implemented exactly as long strings. Set types A set is a bit array where each bit indicates whether an element is in the set or not.
Internal data formats Record types When a record type is declared in the {$A+} state (the default), and when the declaration does not include a packed modifier, the type is an unpacked record type, and the fields of the record are aligned for efficient access by the CPU. The alignment is controlled by the type of each field and by whether fields are declared together. Every data type has an inherent alignment, which is automatically computed by the compiler.
Internal data formats File types File types are represented as records. Typed files and untyped files occupy 332 bytes, which are laid out as follows: type TFileRec = packed record Handle: Integer; Mode: word; Flags: word; case Byte of 0: (RecSize: Cardinal); 1: (BufSize: Cardinal; BufPos: Cardinal; BufEnd: Cardinal; BufPtr: PChar; OpenFunc: Pointer; InOutFunc: Pointer; FlushFunc: Pointer; CloseFunc: Pointer; UserData: array[1..32] of Byte; Name: array[0..
Internal data formats where fmClosed indicates that the file is closed, fmInput and fmOutput indicate a text file that has been reset (fmInput) or rewritten (fmOutput), fmInOut indicates a typed or untyped file that has been reset or rewritten. Any other value indicates that the file variable is not assigned (and hence not initialized). The UserData field is available for user-written routines to store data in.
Internal data formats corresponding virtual method’s entry point. This layout is compatible with a C++ vtable and with COM. At negative offsets, a VMT contains a number of fields that are internal to Delphi’s implementation. Applications should use the methods defined in TObject to query this information, since the layout is likely to change in future implementations of the Delphi language. Table 11.
Internal data formats Variant types A variant is stored as a 16-byte record that contains a type code and a value (or a reference to a value) of the type given by the code. The System and Variants units define constants and types for variants. The TVarData type represents the internal structure of a Variant variable (on Windows, this is identical to the Variant type used by COM and the Win32 API).
Chapter 12 Program control Chapter12 This chapter explains how parameters and function results are stored and transferred. The final section discusses exit procedures. Parameters and function results Treatment of parameters and function results is determined by several factors, including calling conventions, parameter semantics, and the type and size of the value being passed.
Parameters and function results • A long-string or dynamic-array parameter is passed as a 32-bit pointer to the dynamic memory block allocated for the long string. The value nil is passed for an empty long string. • A pointer, class, class-reference, or procedure-pointer parameter is passed as a 32bit pointer. • A method pointer is passed on the stack as two 32-bit pointers. The instance pointer is pushed before the method pointer so that the method pointer occupies the lowest address.
Parameters and function results Register saving conventions Procedures and functions must preserve the EBX, ESI, EDI, and EBP registers, but can modify the EAX, EDX, and ECX registers. When implementing a constructor or destructor in assembler, be sure to preserve the DL register. Procedures and functions are invoked with the assumption that the CPU’s direction flag is cleared (corresponding to a CLD instruction) and must return with the direction flag cleared.
Parameters and function results Method calls Methods use the same calling conventions as ordinary procedures and functions, except that every method has an additional implicit parameter Self, which is a reference to the instance or class in which the method is called. The Self parameter is passed as a 32-bit pointer. • Under the register convention, Self behaves as if it were declared before all other parameters. It is therefore always passed in the EAX register.
Exit procedures Exit procedures Exit procedures ensure that specific actions—such as updating and closing files—are carried out before a program terminates. The ExitProc pointer variable allows you to “install” an exit procedure, so that it is always called as part of the program’s termination—whether the termination is normal, forced by a call to Halt, or the result of a runtime error. An exit procedure takes no parameters.
An exit procedure can learn the cause of termination by examining the ExitCode integer variable and the ErrorAddr pointer variable. In case of normal termination, ExitCode is zero and ErrorAddr is nil. In case of termination through a call to Halt, ExitCode contains the value passed to Halt and ErrorAddr is nil. In case of termination due to a runtime error, ExitCode contains the error code and ErrorAddr contains the address of the invalid statement.
Chapter 13 Inline assembly code Chapter13 The built-in assembler allows you to write assembly code within Delphi programs.
Assembler statement syntax 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 EDI, ESI, ESP, EBP, and EBX registers, but can freely modify the EAX, ECX, and EDX registers. On entry to an asm statement, EBP points to the current stack frame and ESP points to the top of the stack. Except for ESP and EBP, an asm statement can assume nothing about register contents on entry to the statement.
Assembler statement syntax Instruction opcodes The built-in assembler supports all of the Intel-documented opcodes for general application use. Note that operating system privileged instructions may not be supported.
Assembler statement syntax Assembly directives The built-in assembler supports three assembly define directives: DB (define byte), DW (define word), and DD (define double word). Each generates 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.
Assembler statement syntax When an identifier precedes a DB, DW, or DD directive, it causes the declaration of a byte-, word-, or double-word-sized variable at the location of the directive. For example, the assembler allows the following: ByteVar WordVar IntVar ƒ DB DW DD ? ? ? MOV MOV MOV AL,ByteVar BX,WordVar ECX,IntVar The built-in assembler doesn’t support such variable declarations. The only kind of symbol that can be defined in an inline assembly statement is a label.
Assembler statement syntax DMTINDEX retrieves the dynamic method table index of the passed dynamic method. This directive also needs a fully specified class name with a method name as a parameter, for example, TExample.DynamicMethod. To invoke the dynamic method, call System.@CallDynaInst with the (E)SI register containing the value obtained from DMTINDEX. Note Methods with the message directive are implemented as dynamic methods and can also be called using the DMTINDEX technique.
Assembler statement syntax procedure CallVirtualMethod(e: TExample); asm // Instance pointer needs to be in EAX MOV EAX, e // Retrieve VMT table entry MOV EDX, [EAX] // Now call the method at offset VMTOFFSET CALL DWORD PTR [EDX + VMTOFFSET TExample.VirtualMethod] end; var e: TExample; begin e := TExample.Create; try CallDynamicMethod(e); CallVirtualMethod(e); finally e.Free; end; end. Operands Inline assembler operands are expressions that consist of constants, registers, symbols, and operators.
Expressions Reserved words always take precedence over user-defined identifiers. For example, 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 (&) override operator: MOV &Ch, 1 It is best to avoid user-defined identifiers with the same names as built-in assembler reserved words. Expressions The built-in assembler evaluates all expressions as 32-bit integer values.
Expressions the built-in assembler cannot compute the value of X + Y at compile time. In this case, to move the sum of X and Y into Z you would use asm MOV ADD MOV end; EAX,X EAX,Y Z,EAX In a Delphi expression, a variable reference denotes the contents of the variable. But in an assembler expression, a variable reference denotes the address of the variable.
Expressions String constants String constants must be enclosed in single or double quotation marks. Two consecutive quotation marks of the same type as the enclosing quotation marks count as only one character. Here are some examples of string constants: 'Z' 'Delphi' ‘Linux’ "That's all folks" '"That''s all folks," he said.' '100' '"' "'" String constants of any length are allowed in DB directives, and cause allocation of a sequence of bytes containing the ASCII values of the characters in the string.
Expressions Registers The following reserved symbols denote CPU registers in the inline assembler: Table 13.
Expressions The following symbols cannot be used in asm statements: • • • • Standard procedures and functions (for example, WriteLn and Chr). String, floating-point, and set constants (except when loading registers). Labels that aren’t declared in the current block. The @Result symbol outside of functions. The following table summarizes the kinds of symbol that can be used in asm statements. Table 13.
Expressions Identifiers can be qualified within asm statements. For example, given the declarations type TPoint = record X, Y: Integer; end; TRect = record A, B: TPoint; end; var P: TPoint; R: TRect; the following constructions can be used in an asm statement to access fields. MOV MOV MOV MOV EAX,P.X EDX,P.Y ECX,R.A.X EBX,R.B.Y A type identifier can be used to construct variables on the fly. Each of the following instructions generates the same machine code, which loads the contents of [EDX] into EAX.
Expressions Immediate values and memory references cause different code to be generated when used as operands. For example, const Start = 10; var Count: Integer; ƒ asm MOV EAX,Start MOV EBX,Count MOV ECX,[Start] MOV EDX,OFFSET Count end; { { { { MOV MOV MOV MOV EAX,xxxx } EBX,[xxxx] } ECX,[xxxx] } EDX,xxxx } Because Start is an immediate value, the first MOV is assembled into a move immediate instruction.
Expressions Expression types Every built-in assembler expression has a type—or, more correctly, a size, because the assembler regards the type of an expression simply as the size of its memory location. For example, the type of an Integer variable is four, because it occupies 4 bytes.
The following table summarizes the predefined type symbols that the built-in assembler provides in addition to any currently declared Delphi types. Table 13.5 Predefined type symbols Symbol Type BYTE 1 WORD 2 DWORD 4 QWORD 8 TBYTE 10 Expression operators The built-in assembler provides a variety of operators. Precedence rules are different from that of the Delphi language; for example, in an asm statement, AND has lower precedence than the addition and subtraction operators.
Table 13.7 Definitions of built-in assembler expression operators (continued) Operator Description . Structure member selector. The result is the sum of the expression before the period and the expression after the period, with the type of the expression after the period. Symbols belonging to the scope identified by the expression before the period can be accessed in the expression after the period. HIGH Returns the high-order 8 bits of the word-sized expression following the operator.
Table 13.7 Definitions of built-in assembler expression operators (continued) Operator Description OR Bitwise OR. Both expressions must be absolute immediate values, and the result is an absolute immediate value. XOR Bitwise exclusive OR. Both expressions must be absolute immediate values, and the result is an absolute immediate value. Assembly procedures and functions You can write complete procedures and functions using inline assembly language code, without including a begin...end statement.
Assembly language functions return their results as follows. • Ordinal values are returned in AL (8-bit values), AX (16-bit values), or EAX (32-bit values). • Real values are returned in ST(0) on the coprocessor’s register stack. (Currency values are scaled by 10000.) • Pointers, including long strings, are returned in EAX. • Short strings and variants are returned in the temporary location pointed to by @Result.
13-20 Delphi Language Guide
Appendix A Delphi grammar Appendix A Goal -> (Program | Package | Library | Unit) Program -> [PROGRAM Ident ['(' IdentList ')'] ';'] ProgramBlock '.' Unit -> UNIT Ident [PortabilityDirective] ';' InterfaceSection ImplementationSection InitSection '.' Package -> PACKAGE Ident ';' [RequiresClause] [ContainsClause] END '.' Library -> LIBRARY Ident ';' ProgramBlock '.
ImplementationSection -> IMPLEMENTATION [UsesClause] [DeclSection]... [ExportsStmt]... Block -> [DeclSection] [ExportsStmt]... CompoundStmt [ExportsStmt]... ExportsStmt -> EXPORTS ExportsItem [, ExportsItem]... ExportsItem -> Ident [NAME|INDEX “‘” ConstExpr “‘”] [INDEX|NAME “‘” ConstExpr “‘”] DeclSection -> -> -> -> -> LabelDeclSection ConstSection TypeSection VarSection ProcedureDeclSection LabelDeclSection -> LABEL LabelId ConstSection -> CONST (ConstantDecl ';')...
RealType -> -> -> -> -> -> -> REAL48 REAL SINGLE DOUBLE EXTENDED CURRENCY COMP OrdinalType -> (SubrangeType | EnumeratedType | OrdIdent) OrdIdent -> -> -> -> -> -> -> -> -> -> -> -> SHORTINT SMALLINT INTEGER BYTE LONGINT INT64 WORD BOOLEAN CHAR WIDECHAR LONGWORD PCHAR VariantType -> VARIANT -> OLEVARIANT SubrangeType -> ConstExpr '..' ConstExpr EnumeratedType -> '(' EnumeratedTypeElement ','...
VarSection -> VAR (VarDecl ';')... VarDecl On Windows -> IdentList ':' Type [(ABSOLUTE (Ident | ConstExpr)) | '=' ConstExpr] [PortabilityDirective] On Linux -> IdentList ':' Type [ABSOLUTE (Ident) | '=' ConstExpr] [PortabilityDirective] Expression -> SimpleExpression [RelOp SimpleExpression]... SimpleExpression -> ['+' | '-'] Term [AddOp Term]... Term -> Factor [MulOp Factor]...
StructStmt -> -> -> -> -> -> -> -> CompoundStmt ConditionalStmt LoopStmt WithStmt TryExceptStmt TryFinallyStmt RaiseStmt AssemblerStmt CompoundStmt -> BEGIN StmtList END ConditionalStmt -> IfStmt -> CaseStmt IfStmt -> IF Expression THEN Statement [ELSE Statement] CaseStmt -> CASE Expression OF CaseSelector ';'... [ELSE StmtList] [';'] END CaseSelector -> CaseLabel ','... ':' Statement CaseLabel -> ConstExpr ['..
FunctionHeading -> FUNCTION Ident [FormalParameters] ':' (SimpleType | STRING) ProcedureHeading -> PROCEDURE Ident [FormalParameters] FormalParameters -> '(' [FormalParm ';'...
ClassFieldList -> (ClassVisibility ObjFieldList) ';'... ClassMethodList -> (ClassVisibility MethodList) ';'... ClassPropertyList -> (ClassVisibility PropertyList ';')... PropertyList -> PROPERTY Ident [PropertyInterface] [PropertySpecifiers] [PortabilityDirective] PropertyInterface -> [PropertyParameterList] ':' Ident PropertyParameterList -> '[' (IdentList ':' TypeId) ';'...
A-8 Delphi Language Guide
Index Symbols A - 4-4, 4-7, 4-10, 4-11 " 13-10 # 4-5 $ 4-4, 4-6 (*, *) 4-6 (, ) 4-2, 4-13, 4-15, 5-6, 5-45, 6-2, 6-3, 6-11, 7-2, 10-1 * 4-2, 4-7, 4-11 + 4-4, 4-7, 4-9, 4-10, 4-11 , 3-6, 4-25, 5-6, 5-23, 5-25, 6-11, 7-17, 9-6, 9-11, 10-7, 13-2 .
B $B directive 4-9 base types 5-8, 5-18, 5-19, 5-20 begin (reserved word) 3-2, 4-21, 6-2, 6-3 binary operators 4-7 binding fields 7-7 methods 7-10 bitwise operators,not 4-9 blanks 4-1 BlockRead procedure 8-4 blocks 4-29 to 4-30 function 3-4, 6-2, 6-3 library 9-7 outer and inner 4-31 procedure 3-4, 6-2, 6-3 program 3-1, 3-2 scope 4-29 to 4-32 try...except 7-29, 7-32 try...finally 7-33 BlockWrite procedure 8-4 body (routine) 6-2 boldface 1-2 Boolean operators 4-8 complete vs.
CmdLine variable 9-8 COM 10-4 interfaces 10-2, 10-11 to 10-13 out parameters 6-14 variants and 5-33, 5-35, 11-12 COM error handling 6-5 comments 4-1, 4-6 ComObj unit 7-6, 10-12 Comp type 5-10, 11-5 comparison classes 4-12 class-reference types 4-12 dynamic arrays 5-21 integer types 4-12 objects 4-12 packed strings 4-12 PChar type 4-12 real types 4-12 relational operators 4-11 strings 4-12, 5-11 compiler 2-2, 2-3, 2-5, 3-1 command-line 2-3 to 2-5 directives 3-2, 4-6 packages 9-13 complete evaluation 4-8 comp
default properties 7-21 interfaces 10-2 default property (COM object) 5-35 default specifier 7-6, 7-17, 7-22 DefaultHandler method 7-17 defining declarations 6-6, 7-6, 7-8, 10-4 DefWindowProc function 7-17 delegated interface 10-7 delegation (interface implementation) 10-7 Delphi 2-1 $DENYPACKAGEUNIT directive 9-13 dependency, units 3-7 to 3-8 deprecated (directive) 4-18 dereference operator 4-10, 5-20 pointer overview 5-28 variants and 5-35 descendants 7-3, 7-5 $DESIGNONLY directive 9-13 design-time packag
E E (in numerals) 4-4 EAssertionFailed 7-28 else (reserved word) 4-24, 4-26, 7-30 empty set 5-18 end (reserved word) 3-2, 4-21, 4-25, 5-23, 5-24, 6-2, 6-3, 7-2, 7-30, 7-33, 9-10, 10-1, 10-11, 13-1 end-of-line character 4-1, 8-3 enumerated types 5-6 to 5-8, 11-3 anonymous values 5-8, 7-5 publishing 7-5 Eof function 8-6 Eoln function 8-6 equality operator 4-11 ErrorAddr variable 12-6 EStackOverflow exception 11-2 event handlers 2-7, 7-5 events 2-7, 7-5 example programs 2-3 to 2-5 except (reserved word) 7-30 E
G -$G- compiler switch 9-14 $G directive 9-13 generic types 5-1 GetHeapStatus function 11-2 GetMem procedure 5-28, 5-42, 9-9, 11-1, 11-2 GetMemoryManager procedure 11-2 GetProcAddress function 9-2 global identifiers 4-30 global variables 5-41 dynamically loadable libraries 9-8 interfaces 10-10 memory management 11-2 GlobalAlloc 11-1 goto statements 4-20 grammar (formal) A-1 to A-7 GUIDs 10-1, 10-3, 10-10 generating 10-3 H $H directive 5-11, 6-15 Halt procedure 12-5, 12-6 heading program 2-1, 3-1, 3-2 routi
interface declarations 3-4 default paramters 6-20 interface section 3-3, 3-4, 3-7 forward declarations and 6-6 methods 7-8 scope 4-31 uses clause 3-8 interfaces 7-2, 10-1 to 10-13 accessing 10-9 to 10-11 Automation 10-11 calling conventions 10-3 compatibility 10-10 delegation 10-7 dispatch interface types 10-11 dual interfaces 10-13 freeing 5-42 GUIDs 10-1, 10-3, 10-10 implementing 10-4 to 10-8 interface references 10-9 to 10-11 interface types 10-1 to 10-4 memory management 11-2 method resolution clauses 1
dual-interface 6-5 dynamic 7-10, 7-11 implementation 7-8 overloading 7-12 overriding 7-11, 7-12, 10-6 pointers 4-13, 5-31 publishing 7-5 static 7-10 virtual 7-6, 7-10, 7-11 $MINSTACKSIZE directive 11-2 mod 4-7 multibyte character sets 5-13 string-handling routines 8-7 multidimensional arrays 5-19, 5-22, 5-45 multiple unit references 3-7 multiplication 4-7 multithreaded applications 5-42 dynamically loadable libraries 9-8 mutually dependent classes 7-7 mutually dependent units 3-8 N name (directive) 6-7, 6-
P $P directive 6-15 package files 2-2, 2-3, 9-10, 9-13 packages 9-10 to 9-14 compiler directives 9-13 compiler switches 9-14 compiling 9-13 declaring 9-10 loading dynamically 9-10 loading statically 9-10 thread variables 9-11 uses clause and 9-10 packed (reserved word) 5-17, 11-8 packed arrays 4-5, 4-10, 5-19 packed records 11-8 packed strings 5-19 comparison 4-12 pairs of symbols 4-2 PAnsiChar type 5-14, 5-29 PAnsiString type 5-29 parameters 5-31, 6-3, 6-11 to 6-20 actual 6-20 array 6-12, 6-16 array proper
program, control 6-20 program (reserved word) 3-2 program control ?? to 12-6 programs 2-1 to 2-5, 3-1 to 3-8 examples 2-3 to 2-5 syntax 3-1 to 3-3 project files 2-2, 3-1, 3-2, 3-6 Project Manager 2-1 project options files 2-2 projects 2-6, 3-6 properties 7-1, 7-17 to 7-24 access specifiers 7-18 array 7-5, 7-20 as parameters 7-18 declaring 7-17, 7-20 default 7-21, 10-2 interfaces 10-4 overriding 7-6, 7-23 read-only 7-19 record 7-5 write-only 7-19 protected class members 7-4, 7-5 prototypes 6-2 PShortString t
resource strings 5-45 resourcestring (reserved word) 5-45 Result variable 6-3, 6-4 RET instruction 13-3 return type (functions) 6-3, 6-4 return value (functions) 6-3, 6-4, 6-5 constructors 7-13 Rewrite procedure 8-2, 8-4, 8-5, 8-6 routines 6-1 to 6-21 exporting 9-6 standard 8-1 to 8-10 RTTI 7-5, 7-13 $RUNONLY directive 9-13 runtime packages 9-10 S $S directive 11-2 safecall (calling convention) 6-5, 12-2 constructors and destructors 12-4 dual interfaces 10-13 interfaces 10-3 Self 12-4 scope 4-29 to 4-32 cl
StringToWideChar function 8-7 strong typing 5-1 structured statements 4-21 structured types 5-17 files and 5-26 records and 5-25 variants and 5-33 structures 5-23 StrUpper function 5-16 subrange types 4-7, 4-25, 5-8 subset operator 4-11 subtraction 4-7 pointers 4-10 Succ function 5-3 successor 5-3 superset operator 4-11 symbol pairs 4-2 symbols 4-1, 4-2 assembler 13-11 syntax descriptions 1-2 formal A-1 to A-7 System unit 3-1, 3-5, 5-29, 5-34, 5-35, 6-18, 7-3, 7-29, 8-1, 8-7, 10-2, 10-3, 10-5, 10-11, 11-12
set 5-18, 11-7 simple 5-3 string 5-11 to 5-17, 11-5, 11-6 structured 5-17 subrange 5-8 type identity 5-37 user-defined 5-1 variant 5-33 to 5-37 typographical conventions 1-2 U UCS-2 5-14 UCS-4 5-14 unary operators 4-7 Unassigned (variants) 5-33, 5-35 underscores 4-2 Unicode 5-5, 5-13 union (sets) 4-11 UniqueString procedure 5-17 unit files 3-1, 3-3 case-sensitivity 4-2 units 2-1, 3-1 to 3-8 scope 4-31 syntax 3-3 to 3-8 until (reserved word) 4-27 untyped files 5-27, 8-2, 8-4 untyped parameters 6-14 UpCase f
W $WARNINGS directive 4-18 $WEAKPACKAGEUNIT directive 9-13 while statements 4-21, 4-27 wide characters and strings 5-13 memory management 11-2 standard routines 8-7 WideChar type 4-10, 5-5, 5-11, 5-14, 5-29, 11-3 WideCharLenToString function 8-7 WideCharToString function 8-7 WideString type 5-11, 5-13, 5-14, 5-30 memory management 11-6 Windows 7-17 memory management 11-1, 11-2 messages 7-15 variants and 11-12 Windows unit 9-2 with statements 4-21, 4-22, 5-24 Word type 5-4, 11-3 assembler 13-16 WordBool type