Forum > General

Out Parameter - Test program - funny behaviour

(1/5) > >>

jc99:
I have a program/problem:
see: http://bugs.freepascal.org/view.php?id=28279

--- Code: ---program PrjTestParameter;

{$IFDEF MSWINDOWS}
{$APPTYPE CONSOLE}
{$ENDIF}

{ $Define WorkAround1}
{ $Define WorkAround2}
{ $Define WorkAround3}

Function Test({const}Line:String;out Part1:string;{$ifndef WorkAround2} out{$else}var{$endif} Part2:string):Boolean;{$ifdef WorkAround3} inline;{$endif}

begin
  result := length(Line)>1;
  Part1:= copy(Line,1,1);
  Part2:=copy(Line,2,length(Line)-1);
end;

var
  BaseLine: String;
  OneChar: string;
  Baseline2: string;

begin
  BaseLine := 'ABCDEFGHIJKLMNOP';
  while Test(Baseline,OneChar,Baseline) do
    begin
     writeln(Onechar);
{$ifdef WorkAround1}
     BaseLine2:=BAseline;
{$endif}
    end;
  readln;
end.
--- End code ---

In my opinion this should work.
If it is wrong then Workaround1 (not 2, sorry my mistake) should not make it work.

Both workarounds are not satisfying, the second gives a hint that a variable isn't initialized. The first uses an unnecessary variable.

Edit:
In this form, it may be obvious that it is not working ...
but think of it that the procedure is in one unit and the call is in another unit. ... and it's a big project, lot's of files, ton's of code ...
Then think that the procedure started as

--- Code: ---Function Test(Line:String;var Part1,Part2:string):Boolean;
--- End code ---
everything was fine.
The Story goes:
Then a new compiler came and tells you that some variables are not initialized. So you look at the code and say "Oh, yes, these paramaters are only written to so they should be out-Parameters". You change the var to out, compile the program, no error no warning, no hint.
Program running fine. 'A' as baseline works, 'AB' as baseline works.
But then after releasing the Prog somehow you have to do 'ABC' as baseline and BOOM, where is my B and C.

So my point is:
This should either be working, or there should be a Warning that it may be not working.

Edit:
Found Workaround3: declare the function inline

Basile B.:
The problem is that when you use the out parameter storage class, the value you pass is implicitly set to its default value (empty string). example:


--- Code: ---program Project1;

var
  str: string;

procedure MagicReset(out value: string);
begin
end;

begin
  str := 'wxcvbn,;:!';
  MagicReset(str);
  writeln(str);
  readln;
end.   

--- End code ---

But, in your example, when Baseline is magically reset, since ObjectPascal string are reference counted, this reference count is reset to 0. While actually it shouldn't. That's a bug.

read more here, Delphi has (had?) the same problem:
http://alex.ciobanu.org/?p=48

Martin_fr:
About your remark in the linked bug:

--- Code: --- When debugging you will see, that at the end of the first call of test Line still has 'ABCD...', Part1 := 'A', Part2 := 'BCD..'
--- End code ---
Assuming that statement was checked with "const" in code. (const kind of makes the string a pchar, not guaranteeing any ref count)

The debugger does not check if the variable is valid. A string variable is a pointer, and a "const string" can point to the remains of a string. That is it can point to freed memory, that has not yet been overridden and therefore looks like the string you expect.

In case if there is no "const" then it depends on the refcount. (see below)

Actually there is a bug. However that is probably not what you experienced. Because this bug passes in an invalid/trashed string.
--------------

The reason you may (or may not) see the "correct" string in "Line" is the ref count of the variable "Baseline". In the first iteration of the loop, you have a constant (special ref count, afaik -1), that works different. In the 2nd iteration of the loop the refcount should be 1, and things may go wrong.
However any slight change of the code (or me mis-reading it) and the refcount is 2 or higher, and it behaves more like you want (at least it may, no guarantee here).

----------
Even if the issue I mentioned earlier is fixed, your code is still not good.
That is the value of "Line" still depends on the order of argument evaluation. And afaik in your code it will first be set to empty string.
(Well that is assumed the order of param eval is documented, and therefore guaranteed. IIRC it is, but not sure)

I agree your code looks good at first, and it takes a lot of detailed knowledge to find why it does not work.
As I said *IF* the order of param eval is documented (and in this case documented last param is evaled first (at least for pascal calling convention).
1) out param is evaluated => variable is set to empty string.
   **IF** refcount is 1, then it affects the next param, otherwise not.
2) Line is evaluated. This may be the empty string.

jc99:
@Martin_fr

--- Quote ---Assuming that statement was checked with "const" in code. (const kind of makes the string a pchar, not guaranteeing any ref count)

--- End quote ---
No it behaves like this in both ways (with and without "const")

@BBasile

--- Quote ---read more here, Delphi has (had?) the same problem:

--- End quote ---
In Delphi (XE & XE2) it is different, the code does not work at all,
the parameters are reset right away. So you have no problem debugging it.

My point is:
Since the compiler gives so much Hints about not initialized Variables, it should give a Warning when the same parameter is passed to Procedure/Function twice in different manners.
The better solution would be to make the thing work.
1. always evaluate out Parameter last/first (same way)
2. increment the counter, when the parameter is used
or do the Initialization only when it's not initialized.

BUT:

--- Code: ---  BaseLine := 'ABC';
  if test(BaseLine,Onechar,BaseLine) then
    writeln('1: ',Onechar);

  if test(BaseLine,Onechar,BaseLine) then
    writeln('2: ',Onechar);

--- End code ---
(Code fails on second call) in this case it should be obvious to the compiler, that the parameter is used again, but it still fails.

jc99:

--- Quote ---Even if the issue I mentioned earlier is fixed, your code is still not good.
That is the value of "Line" still depends on the order of argument evaluation. And afaik in your code it will first be set to empty string.
(Well that is assumed the order of param eval is documented, and therefore guaranteed. IIRC it is, but not sure)

--- End quote ---
I switched the parameters in the test Function (out,out,const/direct),
and the program/compiler still behaves this way.

Navigation

[0] Message Index

[#] Next page

Go to full version