Ex 03
Ex 03
In this code, the TStopWatch record of the System.Diagnostics unit, a structure that
keep track of the time (or system ticks) elapsed between the Start (or StartNew)
and the Stop calls.
The form has two buttons both calling this same exact code, but one of them has
inlining disabled at the call site. Notice you need to compile with the Release config-
uration to see any difference (as inlining is a Release optimization). With twenty
million interactions (the value of the LoopCount constant), on my computer I get the
following results:
// on Windows (running in a VM)
Max on 20000000 [17]
Max off 20000000 [45]
The Object Pascal compiler doesn’t define a clear cut limit on the size of a function
that can be inlined or a specific list of constructs (for or while loops, conditional
statements) that would prevent inlining. However, since inlining a large function
provides little advantage yet exposes you to the risk of some real disadvantages (in
terms of code bloat), you should avoid it.
One limitation is that the method or function cannot reference identifiers (such as
types, global variables, or functions) defined in the implementation section of the
unit, as they won’t be accessible in the call location. However, if you are calling a
local function, which happens to be inlined as well, then the compiler will accept
your request to inline your routine.
A drawback is that inlining requires more frequent recompilations of units, as when
you modify an inlined function, the code of each of the calling sites will need to be
recompiled as well. Within a unit, you might write the code of the inlined functions
before calling them, but better place them at the beginning of the implementation
section.
note Object Pascal uses a single pass compiler, so it cannot paste in the code of a function it hasn’t
compiled yet.
Within different units, you need to specifically add other units with inlined functions
to your uses statements, even if you don’t call those methods directly. Suppose your
unit A calls an inlined function defined in unit B. If this function in turn calls
another inlined function in unit C, your unit A needs to refer to C as well. If not,
you’ll see a compiler warning indicating the call was not inlined due to the missing
unit reference. A related effect is that functions are never inlined when there are cir-
cular unit references (through their implementation sections).
Procedural Types
Another unique feature of Object Pascal is the presence of procedural types. These
are really an advanced language topic, which only a few programmers will use regu-
larly. However, since we will discuss related topics in later chapters (specifically,
method pointers, a technique heavily used by the environment to define event han-
dlers, and anonymous methods), it's worth giving a quick look at them here.
In Object Pascal (but not in the more traditional Pascal language) there is the con-
cept of a procedural type (which is similar to the C language concept of a function
pointer – something languages like C# and Java have dropped, because it is tied to
global functions).
The declaration of a procedural type indicates the list of parameters and, in the case
of a function, the return type. For example, you can declare a new procedural type,
with an Integer parameter passed by reference, with this code:
type
TIntProc = procedure (var Num: Integer);
This procedural type is compatible with any routine having exactly the same param-
eters (or the same function signature to use C jargon). Here is an example of a
compatible routine:
procedure DoubleTheValue (var Value: Integer);
begin
Value := Value * 2;
end;
Procedural types can be used for two different purposes: you can declare variables of
a procedural type or pass a procedural type (that is, a function pointer) as parameter
to another routine. Given the preceding type and procedure declarations, you can
write this code:
var
IP: TIntProc;
X: Integer;
begin
IP := DoubleTheValue;
X := 5;
IP (X);
end;
This code has the same effect as the following shorter version:
var
X: Integer;
begin
X := 5;
DoubleTheValue (X);
end;
The first version is clearly more complex, so why and when should we use it? There
are cases in which being able to decide which function to call and actually calling it
later on can be very powerful. It is possible to build a complex example showing this
approach. However, I prefer to let you explore a fairly simple application project,
called ProcType.