1

On Emba's website, it is stated that an OUT parameter (does not say which type, so I assume it applies to all types) should not be used for input because its value is "discarded".

Screenshot, to last for eternity :) "enter image description here"

By discarded, I interpret (even though we are not at the Bible Study hour) that whatever value is present in that variable is thrown away and the variable is zeroed.
Update: from the discussion in the comments I think that the English word "disregarded" or "ignored" would be more accurate.

With an out parameter, however, the initial value of the referenced variable is discarded by the routine it is passed to.

But this simple code shows that the value stored in "i" is not zeroed, but a string "s" is. So the behavious of OUT is not only inconsistent but ALSO undocumented.

TYPE TMyRec = record
  i: Integer;
end;

Procedure TestInteger(OUT i: Integer);
Begin
End;

Procedure TestRec(OUT R: TMyRec);
Begin
End;

Procedure TestStr(OUT S: string);
Begin
End;

procedure TfrmTest.btnTestRecClick(Sender: TObject);
begin
  VAR MyRecord: TMyRec;
  MyRecord.i:= 7;
  TestRec(MyRecord);
  Memo.Lines.Add('Test rec: '+ IntToStr(MyRecord.i));
end;

procedure TfrmTest.btnTestStrClick(Sender: TObject);
begin
  VAR s: string:= 'x';
  TestStr(s);
  Memo.Lines.Add('Test str: '+ s);
end;

procedure TfrmTest.btnTestIntClick(Sender: TObject);
begin
  VAR i: Integer:= 7;
  TestInteger(i);
  Memo.Lines.Add('Test int: '+ IntToStr(i));
end;

enter image description here

The documentation also says:

But you're not using MyRecord to pass any data to the GetInfo procedure; MyRecord is just a container where you want GetInfo to store the information it generates. The call to GetInfo immediately frees the memory used by MyRecord, before program control passes to the procedure.

But my test code shows that the memory for me record was not zeroed.

So, is the documentation wrong? Is simply the wording wrong? Maybe they meant "ignored" instead of "discarded"? Why is Emba using such confusing words when better alternatives are available?


Update:
Another confirmation that the behavior of the OUT param is not only poorly documented but also inconsistent: The article shows that managed things are always nullified.
https://delphisorcery.blogspot.com/2021/04/out-parameters-are-just-bad-var.html

Gabriel
  • 20,797
  • 27
  • 159
  • 293
  • Simple types have a random value when allocating memory. Depending on how used the allocated memory was, you get interesting values. However, in your example, the memory that the value is mapped to is initialized with the value 7. Here the documentation is more of a guideline than a law. – USauter Nov 17 '22 at 15:20
  • Discarded is not the same as cleared. You can find additional information on behavior of out parameters at https://delphisorcery.blogspot.com/2021/04/out-parameters-are-just-bad-var.html and https://dalijap.blogspot.com/2021/03/combining-const-and-out-parameters.html – Dalija Prasnikar Nov 17 '22 at 15:21
  • @DalijaPrasnikar - Right in that section, they do clearly say that they clear the mem. Don't they? "The call to GetInfo immediately frees the memory" – Gabriel Nov 17 '22 at 15:24
  • If you put the word "discarded" on a line, between "ignored" and "cleared", I think discarded almost touches the "cleared". – Gabriel Nov 17 '22 at 15:26
  • @ServerOverflow: free <> fill with zeros. If you have `type TX = class A: Integer; end; var X: TX; begin X := TX.Create; try X.A := 394; finally X.Free; end;` then you clearly "free" `X`, but very likely `X.A` will be `394` for a while until the memory is overwritten by something else (which it can be at any time -- you MUST not use (the old object pointed to by) `X` after it has been freed). – Andreas Rejbrand Nov 17 '22 at 15:30
  • I responded to what you quoted here. I didn't read the rest. What is said there is not entirely correct in sense that it is not true for all types. Zeroing is only valid for managed types that need to be initialized to zero or would be broken otherwise. and this is only because C++ side requires it. – Dalija Prasnikar Nov 17 '22 at 15:32
  • I think they wanted to say "disregarded" instead of "discarded". – Gabriel Nov 21 '22 at 15:32
  • https://www.google.com/search?q=define:discarded - "To get rid of something as no longer useful". – Gabriel Nov 21 '22 at 15:34
  • Probably this article also helped putting in my mind that an Out param is always initialized. But again, I first blame the wording used by the documentation. https://delphisorcery.blogspot.com/2021/04/out-parameters-are-just-bad-var.html – Gabriel May 05 '23 at 12:00

2 Answers2

3

Decorating a parameter with out means that the parameter will only be used to return a value to the caller; the routine must not make any assumption about the initial value of the parameter.

The out decoration serves as a semantic hint to the programmer. In addition, the compiler may use the knowledge that the "passed" value will not be used to optimize the generated code.

However, there is no guarantee whatsoever that a "passed" value is indeed cleared. After all, such a guarantee would serve no purpose; on the contrary, it would likely force the compiler to generate slightly less efficient code (because zeroing memory takes time).

Think of a watch manufacturer that makes watches able to function properly down to −50°C. You then make an order for a watch that only needs to be able to function properly down to −20°C. The manufacturer may use this knowledge to produce the watch more cheaply. However, there is absolutely no guarantee that the watch you are delivered will malfunction at −50°C, nor would you typically need such a guarantee. (But of course you must not use the watch below −20°C.)

With an out parameter, however, the initial value of the referenced variable is discarded by the routine it is passed to.

Well, since the programmer never uses the value, it is effectively "discarded", isn't it?

A bit later down on the same page:

The call to GetInfo immediately frees the memory used by MyRecord, before program control passes to the procedure.

Okay, this is hard not to consider as erroneous.

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • "Okay, this is hard not to consider as erroneous" - Right! Emba clearly says that the record is cleared (but it is not). I extended that erroneous info to all types passed as Out. – Gabriel Nov 17 '22 at 15:21
  • 2
    @ServerOverflow: Actually, "clear" <> "free". Typically when something is "freed", the memory is still intact for a while, before it is overwritten, which it can become at any time. It is not filled with zeros. Still, I don't understand what Emba means by "free" at all here – Andreas Rejbrand Nov 17 '22 at 15:26
  • It is possible they used free to emphasize that you cannot pass any input value through out parameter. It seems like this is just poor choice of words. – Dalija Prasnikar Nov 17 '22 at 15:35
  • "since the programmer never uses the value, it is effectively discarded" - Personally I would use a better word, like, "ignored". – Gabriel Nov 22 '22 at 07:47
  • Rather than "discarded" or "ignored," I think a more accurate term would be "not used." The compiler-managed types are _cleared_, which might involve freeing. The point is that any values received by the function are set — by the caller — to values that are safe to overwrite without regard for reference counting or other lifetime management. For `Integer` values, there's no new value required because they're always safe to overwrite. For `string` values, they're cleared. The documentation using "free" seems like it's for a "lay" reader who doesn't need technical details — an odd style choice. – Rob Kennedy Jan 30 '23 at 16:47
1

Note that if you use a managed record it does call initialize and a 0 ends up in the caption.

TYPE TMyRec = record
  i: Integer ;
  class operator Initialize (out Dest: TMyRec);
end;

class operator TMyRec.Initialize (out Dest: TMyRec);
begin
  dest.i := 0;
end;

This is more of a comment but code doesn't format well in comments.

Brian
  • 6,717
  • 2
  • 23
  • 31