Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2023-04

Declaring an array as var or not in function calls?

6 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bo Berglund

I am wondering how to deal with this var or not in function calls:

In a processing program where I am building the command arguments for a write operation by analyzing incoming commands and splitting the data such that the write can be fully done by executing a series of smaller write commands for chunks of data.

The container for this is a dynamic array of write commands and this array is part of the arguments for the build function.

Like this (simplified):

type
  TWriteBytesArgs = record
    DeviceAddr: byte;
    StartAddr: uint16;
    SourceAddr: uint16;
    NumWriteBytes: uint16;
    NextSourceAddr: uint16;
  end;
  TWriteCommands = array of TWriteBytesArgs;
function BuildArgumentsArray(Arg: TWriteBytesArgs; var WArgs: TWriteCommands):
boolean;
var
  variables...
begin
  stuff...
  (Build arguments into WArgs)
end;
procedure main();
var
  WCmd: TWriteBytesArgs;
  WCmds: TWriteCommands;
  Data: TBytes; //Container for actual data
  i: integer;
begin
  ...
  - Fill Data array with values to write
  - Set values to WCmd record members
  if BuildArgumentsArray(WCmd, WCmds) then
  begin
    for i := 0 to Length(WCmds) -1 do
      WriteItem(WCmd[i].DeviceAddr, WCmds[i].StartAddr,
                WCmd[i].SourceAddr, WCmds[i]NumWriteBytes, Data);
    etc...

In main() the Arg record is analyzed by a call to BuildArgumentsArray and based on that one or several actual smaller write commands will be generated. These are written to the WArgs array.

So the WArgs dyn array is originally length zero, but inside the BuildArgumentsArray function its length will be set to contain the needed write commands to do the complete write.
And the caller can iterate over the array items and generate a write command for each.

Now my question:
----------------
Since I am modifying the WArgs variable inside the BuildArgumentsArray function and using its new content afterwards in the WriteItem function I assumed that I had to use var as shown above, but I am not certain this is the case...

It seems like it works also if I do not use the vars specification...

What is the deal here, is this kind of variable written to inside a called function not needing to be set as var and it will work anyway?

And if so, what is the rule for *when* to use var in function call argument lists for items that will be changed inside the called function and used afterwards?

Bo Berglund Developer in Sweden
Giuliano Colla
to Bo Berglund

Il 12/04/2023 21:17, Bo Berglund via fpc-pascal ha scritto:

And if so, what is the rule for*when* to use var in function call argument lists for items that will be changed inside the called function and used afterwards?

Your Wargs is defined as an open array, i.e. its length is unknown at compile time. Therefore the compiler cannot pass it in the subroutine call other than a pointer to the array. It cannot pass the array content because it doesn't know its length.
In that case you may omit the var specifier.
But if one day you change your mind, and decide to establish a size for your array, without var the compiler may decide to pass the full array data to the procedure, and therefore whatever you do in your procedure is done on the local copy of the array and not in the original array. My suggestion is to specify "var" whenever your procedure is expected to modify the data passed. It makes the program more readable, even if "var" is redundant.

Giuliano

Do not do to others as you would have them do to you.They might have different tastes.
Sven Barth
to Giuliano Colla

Giuliano Colla via fpc-pascal schrieb am
Mi., 12. Apr. 2023, 22:54:

Show quoted text 19 lines
Il 12/04/2023 21:17, Bo Berglund via fpc-pascal ha scritto: And if so, what is the rule for **when** to use var in function call argument lists for items that will be changed inside the called function and used afterwards? Your Wargs is defined as an open array, i.e. its length is unknown at compile time. Therefore the compiler cannot pass it in the subroutine call other than a pointer to the array. It cannot pass the array content because it doesn't know its length. In that case you may omit the var specifier. But if one day you change your mind, and decide to establish a size for your array, without var the compiler may decide to pass the full array data to the procedure, and therefore whatever you do in your procedure is done on the local copy of the array and not in the original array. My suggestion is to specify "var" whenever your procedure is expected to modify the data passed. It makes the program more readable, even if "var" is redundant.

There is *no* open array in the example given. Also even if there were changing the length of an open array would not work...

Regards,
Sven

Sven Barth
to Bo Berglund: I am wondering how to deal with this var or not in function calls: In a processing…

Bo Berglund via fpc-pascal schrieb am
Mi., 12. Apr. 2023, 21:17:

Show quoted text 8 lines
Now my question: ---------------- Since I am modifying the WArgs variable inside the BuildArgumentsArray function and using its new content afterwards in the WriteItem function I assumed that I had to use var as shown above, but I am not certain this is the case...

If you modify the length or the contents of a dynamic array parameter then you need to use "var" or "out" modifiers (the later only if you don't need the data passed in).

It seems like it works also if I do not use the vars specification...

It should not. If it does it will likely be an implementation detail and you shall not rely on it. Do you have an explicit example for this behavior?

And if so, what is the rule for *when* to use var in function call argument

lists for items that will be changed inside the called function and used afterwards

The rule is simple: if you need to modify the parameter value itself (e.g. changing a primitive type, changing a field in a record, changing an array's length or data, changing the value of a class instance - but not its fields!) you need to pass it as "var". If it's a write only parameter you can even use "out".

Regards,
Sven

Bo Berglund
to Sven Barth
Sven Barth wrote 14 lines
Bo Berglund via fpc-pascal schrieb am Mi., 12. Apr. 2023, 21:17: > Now my question: > ---------------- > Since I am modifying the WArgs variable inside the BuildArgumentsArray > function and using its new content afterwards in the WriteItem function > I assumed that I had to use var as shown above, but I am not certain this > is the case... > If you modify the length or the contents of a dynamic array parameter then you need to use "var" or "out" modifiers (the later only if you don't need the data passed in).

In this case I am assuming that there is no existing data because I change the length of the array depending on the calculation of the number of needed calls to complete the write operation.
So it is definitely only an "out" argument (I have never used out before...).

Show quoted text 7 lines
> It seems like it works also if I do not use the vars specification... > It should not. If it does it will likely be an implementation detail and you shall not rely on it. Do you have an explicit example for this behavior?

Well, I think I have done this earlier but now I have made a quick test using TBytes and it seems like var *is* needed...

Since I have not used out before I also tested that and it worked like var does.

What is the use case for "out" compared to "var" when it seemingly is doing the same thing?
Or is it exactly this:
With "out" you get a pristine empty variable which you have to initialize if needed (setting its length in this case)?
And you are ensured no stale data will be part of it?

Show quoted text 11 lines
And if so, what is the rule for *when* to use var in function call argument > lists for items that will be changed inside the called function and used > afterwards > The rule is simple: if you need to modify the parameter value itself (e.g. changing a primitive type, changing a field in a record, changing an array's length or data, changing the value of a class instance - but not its fields!) you need to pass it as "var". If it's a write only parameter you can even use "out".

So "out" it is unless I need to use the existing data or append new data to it, in which case I need "var", right?

Thanks!

Bo Berglund Developer in Sweden
Sven Barth
to Bo Berglund

Bo Berglund via fpc-pascal schrieb am
Do., 13. Apr. 2023, 07:49:

Show quoted text 9 lines
What is the use case for "out" compared to "var" when it seemingly is doing the same thing? Or is it exactly this: With "out" you get a pristine empty variable which you have to initialize if needed (setting its length in this case)? And you are ensured no stale data will be part of it?

When the compiler finds an "out" parameter in a call it does not need to check whether the variable you pass is has been written to. With a "var" parameter you'll get "variable not initialized" warnings/hints instead. It's the task of the called function then to provide a correct value for the parameter.

Show quoted text 17 lines
> >And if so, what is the rule for *when* to use var in function call argument >> lists for items that will be changed inside the called function and used >> afterwards >> > >The rule is simple: if you need to modify the parameter value itself (e.g. >changing a primitive type, changing a field in a record, changing an >array's length or data, changing the value of a class instance - but not >its fields!) you need to pass it as "var". >If it's a write only parameter you can even use "out". So "out" it is unless I need to use the existing data or append new data to it, in which case I need "var", right?

Correct.

Regards,
Sven