Ex 02
Ex 02
Ex 02
This is not a common scenario, and I had to build a rather illogical example to show
it to you, but it is worth considering the case (as it does happen occasionally in real
world).
Suppose you decide to implement two overloaded functions to add integers and
floating point numbers:
function Add (N: Integer; S: Single): Single; overload;
begin
Result := N + S;
end;
tip In the errors pane of the IDE you'll see an error message with the first line above, and a plus sign
on the side you can expand to see the following two lines with the details of which overloaded
functions the compiler is considering ambiguous.
If this is a real world scenario, and you need to make the call, you can add a manual
type conversions call to solve the problem and indicate to the compiler which of the
versions of the function you want to call:
Show (Add (10, 10.ToSingle).ToString);
A particular case of ambiguous calls can happen if you use variants, a rather peculiar
data type I'll cover only later in the book.
Default Parameters
Another feature related to overloading, is the possibility of giving a default value to
some of the parameters of a function, so that you can call the function with or with-
out those parameters. If the parameter is missing in the call, it will take the default
value.
Let me show an example (still part of the OverloadTest application project). We can
define the following encapsulation of the Show call, providing two default parame-
ters:
procedure NewMessage (Msg: string;
Caption: string = 'Message';
Separator: string = ': ');
begin
Show (Caption + Separator + Msg);
end;
With this definition, we can call the procedure in each of the following ways:
NewMessage ('Something wrong here!');
NewMessage ('Something wrong here!', 'Attention');
NewMessage ('Hello', 'Message', '--');
This is the output:
Message: Something wrong here!
Attention: Something wrong here!
Message--Hello
Notice that the compiler doesn't generate any special code to support default param-
eters; nor does it create multiple (overloaded) copies of the functions or procedure.
The missing parameters are simply added by the compiler to the calling code. There
is one important restriction affecting the use of default parameters: You cannot
"skip" parameters. For example, you can't pass the third parameter to the function
after omitting the second one.
There are a few other rules for the definition and the calls of functions and proce-
dures (and methods) with default parameters:
• In a call, you can only omit parameters starting from the last one. In other words,
if you omit a parameter you must also omit the following ones.
• In a definition, parameters with default values must be at the end of the parame-
ters list.
• Default values must be constants. Obviously, this limits the types you can use
with default parameters. For example, a dynamic array or an interface type can-
not have a default parameter other than nil; records cannot be used at all.
Inlining
Inlining Object Pascal functions and methods is a low-level language feature that
can lead to significant optimizations. Generally, when you call a method, the com-
piler generates some code to let your program jump to a new execution point. This
implies setting up a stack frame and doing a few more operations and might require
a dozen or so machine instructions. However, the method you execute might be very
short, possibly even an access method that simply sets or returns some private field.
In such a case, it makes a lot of sense to copy the actual code to the call location,
avoiding the stack frame setup and everything else. By removing this overhead, your
program will run faster, particularly when the call takes place in a tight loop exe-
cuted thousands of times.
For some very small functions, the resulting code might even be smaller, as the code
pasted in place might be smaller than the code required for the function call. How-
ever, notice that if a longer function is inlined and this function is called in many
different places in your program, you might experience code bloat, which is an
unnecessary increase in the size of the executable file.
In Object Pascal you can ask the compiler to inline a function (or a method) with the
inline directive, placed after the function (or method) declaration. It is not neces-
sary to repeat this directive in the definition. Always keep in mind that the inline
directive is only a hint to the compiler, which can decide that the function is not a
good candidate for inlining and skip your request (without warning you in any way).
The compiler might also inline some, but not necessarily all, of the calls of the func-
tion after analyzing the calling code and depending on the status of the $INLINE
directive at the calling location. This directive can assume three different values
(notice that this feature is independent from the optimization compiler switch):
● With {$INLINE OFF} you can suppress inlining in a program, in a portion of
a program, or for a specific call site, regardless of the presence of the inline
directive in the functions being called.
● With default value, {$INLINE ON}, inlining is enabled for functions marked
by the inline directive.
● With {$INLINE AUTO} the compiler will generally inline the functions you
mark with the directive, plus automatically inline very short functions.
Watch out because this directive can cause code bloat.
There are many functions in the Object Pascal Run-Time Library that have been
marked as inline candidates. For example, the Max function of the System.Math unit
has definitions like:
function Max(const A, B: Integer): Integer;
overload; inline;
To test the actual effect of inlining this function, I’ve written the following loop in
the InliningTest application project:
var
sw: TStopWatch;
I, J: Integer;
begin
J := 0;
sw := TStopWatch.StartNew;
for I := 0 to LoopCount do
J := Max (I, J);
sw.Stop;
Show ('Max ' + J.ToString +
' [' + sw.ElapsedMilliseconds.ToString + '] ');