Recent

Author Topic: [SOLVED] var and const procedure parameters  (Read 7549 times)

Eugene Loza

  • Hero Member
  • *****
  • Posts: 674
    • My games in Pascal
[SOLVED] var and const procedure parameters
« on: August 19, 2017, 02:55:31 am »
Which way is more efficient?
Code: Pascal  [Select][+][-]
  1. procedure DoSomething(a: TSomeData);
  2. procedure DoSomething(var a: TSomeData);
  3. procedure DoSomething(const a: TSomeData);
where TSomeData may vary greatly from a byte to a large array.
AFAIU first variant is efficient in data use, but not transfer, second is efficient in data transfer but not use (as a will not be local) and the third lets the compiler optimize between two of the above? Or did I get the Wiki article wrong?
Will it have any difference when passing pointers (e.g. to an object)?
« Last Edit: August 19, 2017, 04:19:56 am by Eugene Loza »
My FOSS games in FreePascal&CastleGameEngine: https://decoherence.itch.io/ (Sources: https://gitlab.com/EugeneLoza)

michalis

  • Full Member
  • ***
  • Posts: 140
    • Castle Game Engine
Re: var and const procedure parameters
« Reply #1 on: August 19, 2017, 04:11:39 am »
The differences between these three are not only in efficiency, they are also in whether you can change the value of A inside DoSomething, and what happens with the passed value. You should not choose between one of these based only on the efficiency -- you should choose the one that matches "what you want to do" :)

The full explanation of this may get a little complicated if you consider all the possible things that TSomeData may be (simple integer/float/enum, a class, a record, an AnsiString...), I tried to keep my answer below simple :) More information e.g. in FPC docs on https://www.freepascal.org/docs-html/current/ref/refse91.html

1.

Code: [Select]
procedure DoSomething(A: TSomeData);

When called as DoSomething(B), the compiler creates a copy of B and places it inside your parameter A. This allows you to freely change the value of A inside the DoSomething implementation, without affecting B.

E.g.

Code: [Select]
procedure DoSomething(A: Integer);
begin
  A := A + 10;
  Writeln(A);
end;

var
  B: Integer;
begin
  B := 20;
  DoSomething(B); // will write 30
  // B is still 20 here
end.

This is sometimes useful, if you really want to modify A inside DoSomething, without affecting the B. But if you don't want to modify A inside the DoSomething implementation, then using "const" is usually safer (compiler will not let you modify A inside the procedure) and may be more efficient (the "const" parameter may be internally passed by only passing a pointer to it, not copying it's value, in case if TSomeData size is larger).

Note that if TSomeData is a class (like TObject, TStringList...) then we're only talking here about copying the reference to the class contents. A and B will still point (and least initially) to the same instance. Things are different in case TSomeData is a record, in which case A and B will be just different values. The rule to remember here is that this "copy" is exactly the same as when you do "A := B" in code.

As far as efficiency, the compiler must create a copy of the value. So this is not efficient for larger TSomeData (when TSomeData is something larger than a pointer) -- well, unless you really wanted to create a copy, to be able to modify it locally inside DoSomething.

2.

Code: [Select]
procedure DoSomething(var A: TSomeData);

When called as DoSomething(B), the compiler internally passes a pointer to B. Accessing A (reading, writing) is now the same thing as accessing B. You can change A inside DoSomething implementation, and it immediately changes also B.

Code: [Select]
procedure DoSomething(var A: Integer);
begin
  A := A + 10;
  Writeln(A);
end;

var
  B: Integer;
begin
  B := 20;
  DoSomething(B); // will write 30
  // B is now 30
end.

So this is very efficient (only a pointer is passed, regardless of what TSomeData is). Whether you want it -- depends on what you wanted to achieve.

3.

Code: [Select]
procedure DoSomething(const A: TSomeData);

In this case, you cannot change A inside DoSomething.

It is efficient, and the compiler decides whether internally a pointer to TSomeData is passed, or a just a copy of TSomeData is made. In this sense, indeed the compiler internally decides between doing something like option 1. (copying the value, which makes sense if it's something smaller than the pointer) or 2. (passing the pointer to the value). But what you can do with A differs from both option 1. and option 2. (as now you cannot modify "A" at all inside DoSomething).

There are also other options:

4.

Code: [Select]
procedure DoSomething(out A: TSomeData);

"out A" is usually similar to "var A", but it tells the compiler that the initial value of the parameter (when the DoSomething is called) doesn't matter, and the implementation of DoSomething should always set it. It *may* make the parameter passed differently. It will also change the warnings/hints generated by the compiler.

So this is as efficient as option 2.

5.

Code: [Select]
procedure DoSomething(constref A: TSomeData);

"constref A" is like "const A", but forces the compiler to pass a pointer to A internally. This is usually not needed (using "const" leaves compiler more flexibility, and is usually as good for you).

This is as efficient as option 2.

Eugene Loza

  • Hero Member
  • *****
  • Posts: 674
    • My games in Pascal
Re: var and const procedure parameters
« Reply #2 on: August 19, 2017, 04:19:45 am »
Thanks a lot, Michalis!
That was exactly what I wanted to know! :)
My FOSS games in FreePascal&CastleGameEngine: https://decoherence.itch.io/ (Sources: https://gitlab.com/EugeneLoza)

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: [SOLVED] var and const procedure parameters
« Reply #3 on: August 19, 2017, 09:54:52 am »
One remark:
Quote
4.

Code: Pascal  [Select][+][-]
  1. procedure DoSomething(out A: TSomeData);

"out A" is usually similar to "var A", but it tells the compiler that the initial value of the parameter (when the DoSomething is called) doesn't matter, and the implementation of DoSomething should always set it. It *may* make the parameter passed differently. It will also change the warnings/hints generated by the compiler.

So this is as efficient as option 2.
Although you write "it does not matter" it can not be nil. There needs to be a valid reference other than nil and of the correct type. It is ok if the receiving reference is typed and nil.
« Last Edit: August 19, 2017, 11:08:29 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: [SOLVED] var and const procedure parameters
« Reply #4 on: August 19, 2017, 11:21:48 am »
Although you write "it does not matter" it can not be nil. There needs to be a valid reference other than nil and of the correct type. It is ok if the receiving reference is typed and nil.

The value of the parameter before the call is irrelevant. It can be Nil or some other value. It is discarded/ignored whatever it is. It is only inside the procedure that it must be initialized to a valid value.

The parameter has to be the correct type, of course. This is nothing specifically to do with "out".
The compiler checks the type of all parameters, and baulks if they do not match whether they are passed by value or reference, and whether they are var, out, const or constref.

 

TinyPortal © 2005-2018