Cha 04
Cha 04
Cha 04
note Oddly enough, Object Pascal does allow you to change the value of a typed constant at run-time,
as if it was a variable but only if you enable the $J compiler directive, or use the corresponding
Assignable typed constants compiler option. This optional behavior is included for backward
compatibility of code which was written with an old compiler. This is clearly not a suggested cod-
ing style, and I've covered it in this note most as a historical anecdote about such programming
techniques.
resourcestring
strAuthorName = 'Marco';
begin
ShowMessage (strAuthorname);
In both cases you are defining a constant; that is, a value you don't change during
program execution. The difference is only in the internal implementation. A string
constant defined with the resourcestring directive is stored in the resources of the
program, in a string table.
In short, the advantages of using resources are more efficient memory handling per-
formed by Windows, a corresponding implementation for other platforms, and a
better way of localizing a program (translating the strings to a different language)
without having to modify its source code. As a rule of thumb, you should use
resourcestring for any text that is shown to users and might need translating, and
internal constants for every other internal program string, like a fixed configuration
file name.
tip The IDE editor has an automatic refactoring you can use to replace a string constant in your code
with a corresponding resourcestring declaration. Place the edit cursor within a string literal and
press Ctrl+Shift+L to activate this refactoring.
Data Types
In Pascal there are several predefined data types, which can be divided into three
groups: ordinal types, real types, and strings. We'll discuss ordinal and real types in
the following sections, while strings will be specifically covered in Chapter 6.
Delphi also includes a non-typed data type, called variant, and other “flexible”
types, such as TValue (part of the enhanced RTTI support). Some of these more
advanced data types will be discussed later in Chapter 5.
The Int64 type represents integer numbers with up to 18 digits. This type is fully
supported by some of the ordinal type routines (such as High and Low), numeric rou-
tines (such as Inc and Dec), and string-conversion routines (such as IntToStr) of
the run time library.
operations that you apply using “dot notation”. This is the notation generally used to
apply methods to objects.
note Technically these operations on native data types are defined using “intrinsic record helpers”.
Class and record helpers are covered in Chapter 12. In short, you can customize the operations
applicable to core data types. Expert developers can notice that type operations are defined as
class static methods in the matching intrinsic record helper.
You can see a couple of examples in the following code extracted from the Inte-
gersTest demo:
var
N: Integer;
begin
N := 10;
Show (N.ToString);
// display a constant
Show (33.ToString);
note The Show function used in this code snippet is a simple procedure used to display some string out-
put in a memo control, to avoid having to close multiple ShowMessage dialogs. A side advantage is
this approach makes easier to copy the output and paste in the text (as I've done below). You'll see
this approach used through most of the demos of this book.
There are other operations you can apply to the Integer type (and most other
numerical types), such as:
Size The number of bytes required to store a variable of this type
Parse Convert a string to the numeric value it represents
TryParse Try to convert the string a a number
note C and C++ programmers should notice that the two versions of the Inc procedure, with one or
two parameters, correspond to the ++ and += operators (the same holds for the Dec procedure
which corresponds to the -- and -= operators). The Object Pascal compiler optimizes these incre-
ment and decrement operations, similarly to the way C and C++ compilers do.
Notice that some of these routines are automatically evaluated by the compiler and
replaced with their value. For example, if you call High(X) where X is defined as an
Integer, the compiler replaces the expression with the highest possible value of the
Integer data type.
In the IntegersTest application project I've added an event with a few of these ordi-
nal type functions:
var
n: UInt16;
begin
n := Low (UInt16);
Inc (n);
Show (IntToStr (n));
Inc (n, 10);
Show (IntToStr (n));
if Odd (n) then
Show (IntToStr (n) + ' is odd');
This is the output you should see:
1
11
11 is odd
You can change the data type from Uint16 to Integer or other ordinal types to see
how the output changes.
Out-Of-Range Operations
A variable like n above has only a limited range of valid values. If the value you
assign to it is negative or too big, this results in an error. There are actually three dif-
ferent types of errors you can encounter with out-of-range operations.
The first type of error is a compiler error, which happens if you assign a constant
value (or a constant expression) that is out of range. For example, if you add to the
code above:
n := 100 + High (n);
the compiler will issue the error:
[dcc32 Error] E1012 Constant expression violates subrange bounds
The second scenario takes place when the compiler cannot anticipate the error con-
dition, because it depends on the program flow. Suppose we write (in the same piece
of code):
Inc (n, High (n));
Show (IntToStr (n));
The compiler won't trigger an error because there is a function call, and the compiler
doesn't know its effect in advance (and the error would also depend on the initial
value of n). In this case there are two possibilities. By default, if you compile and run
this application, you'll end up with a completely illogical value in the variable (in this
case the operation will result in subtracting 1!). This is the worst possible scenario,
as you get no error, but your program is not correct.