Combining const and out parameters
This is a continuation of a previous blog post, Are const parameters
dangerous?
Basically, adding an out
parameter into the mix does not change anything that
has been said in the previous post about using const
parameters. But out
parameters, with their own side-effects, create a specific situation that requires
additional explanation.
First, let's see how an out
parameter works.
An out
parameter is a var
parameter with a twist. Just like a var
parameter
passes the original variable as a reference (pointer), so does the out
parameter,
but out
also tells the compiler that the original value can be discarded, since it is
not for passing any input data to the procedure, and will be only used for
passing the result out of the procedure. For managed types, the compiler will clear the original contents
of the out
parameter at the call site.
procedure OutInt(out p: integer);
begin
p := 5;
end;
procedure OutString(out p: string);
begin
p := '5';
end;
procedure Test;
var
x: integer;
s: string;
begin
x := 2;
s := '2';
OutInt(x);
Writeln(x);
OutString(s);
Writeln(s);
end;
5
5
If we remove all code from the procedures, the string parameter will be cleared, but the integer will contain the previously assigned value:
procedure OutInt(out p: integer);
begin
end;
procedure OutString(out p: string);
begin
end;
2
In combination with a const
parameter, out
can have unexpected results:
procedure InOutString(const s: string; out r: string);
begin
r := s + s;
end;
procedure Test;
var
s: string;
begin
s := Copy('abc', 1, 1);
InOutString(s, s);
Writeln(s);
end;
Now, one would expect that the resulting string would have the value aa
, but instead
you will just get an empty string. That is because the output string variable has been
cleared before the InOutString
procedure is called, and adding an empty string to an empty
string will result in an empty string.
Let's remove the const
parameter:
procedure InOutString(s: string; out r: string);
begin
r := s + s;
end;
If you thought the above code will work, you thought wrong. const
is not the
culprit here - out
is - and replacing the out
with var
, while
keeping const
, will give the expected output.
procedure InOutString(const s: string; var r: string);
begin
r := s + s;
end;
I am not going to cover other managed types in detail here. You can try it yourself, but don't expect miracles. On the contrary, with interfaces you can fully expect that the whole thing will just blow up right in your face. For instance, using the following procedure and then calling it with the same interface reference for input and output:
procedure InOutFoo(const f: IFoo; out r: IFoo);
begin
r := TFoo.Create(f.GetNumber * 2);
end;
So... in a const
-out
combination, it turns out that const
is not the real
source of problems. The real question is:
Are out
parameters dangerous?
And the answer is... NO...
Just as with const
parameters, the real culprit are not parameter modifiers, but
the bad coding practice of passing the same variable as input and output
parameters. It is just bad code.
It can be easily broke, it obscures the intent of the code, and it is like drinking from and peeing into the same bottle. Don't do that.
Comments
Post a Comment