Jump to content
havrlisan

Using inline variables inside loops

Recommended Posts

Will there be any issues with declaring inline variables inside loops? Here's an example:
 

procedure Sample(const AFixedArray: TSomeFixedArray);
begin
  for var I = 0 to 10 do
  begin
    var LInlineVariable := TSomeFixedArray[I];
    // do something
  end
end

 

Is it possible that the compiler will allocate more memory than needed, or something in that context? This seems to me like the only way the inline variable feature may produce issues.

Share this post


Link to post

you're declaring the same variable 10 times, I don't think it's possible the way you mean, you could alternatively use a record type, and then call it in an array, like:
 

type
  myRec = Record
          myVar : String[20];
          End;

[...]

var
  ArrRec : Array Of myRec;

[...]

procedure Sample(const AFixedArray: TSomeFixedArray);
begin
  SetLength(ArrRec,10);

  for var I = 0 to 10 do
  begin
    ArrRec[I].myVar := 'xyz'+I.ToString;
    // do something
  end
end

 

Share this post


Link to post
26 minutes ago, Minox said:

you're declaring the same variable 10 times, I don't think it's possible the way you mean, you could alternatively use a record type, and then call it in an array, like:
 


type
  myRec = Record
          myVar : String[20];
          End;

[...]

var
  ArrRec : Array Of myRec;

[...]

procedure Sample(const AFixedArray: TSomeFixedArray);
begin
  SetLength(ArrRec,10);

  for var I = 0 to 10 do
  begin
    ArrRec[I].myVar := 'xyz'+I.ToString;
    // do something
  end
end

 

That's just unnecessarily complicating things. I'd generally declare the variable before the loop (whether it be before begin or as an inline variable, it doesn't matter), I am just wondering if the compiler is able to interpret the variable declaration as it should (?) in loops.

Share this post


Link to post

firstly, I think that not right way to do it, but, as the var is allocated on stack, and the var-life is "short"!

then, the next round, var will "dead", and create a new var on memory same address (if not used yet)!

 

bds_QiyfQ9uYSw.gif

  • Like 1
  • Thanks 2

Share this post


Link to post
1 hour ago, programmerdelphi2k said:

firstly, I think that not right way to do it, but, as the var is allocated on stack, and the var-life is "short"!

then, the next round, var will "dead", and create a new var on memory same address (if not used yet)!

 

bds_QiyfQ9uYSw.gif

Very good explanation, thank you!

Share this post


Link to post

Sorry for reviving this thread, but I came across another usage issue with inline variables inside a for loop:

var LPair: TPair<string, string>;
for LPair in FDict do
  begin
    var LKey: string := LPair.Key;
    FList.Add(procedure
      begin
      	FDict.Remove(LKey);
      end);
  end;

 

In this code I expected for LKey variable to stay on the stack as it is used in the anonymous procedure below, however, LKey will always end up with whichever value is in the last loop. Does this mean that declaring a variable inside a loop will only reinitialize it every time at the same address?

 

Edit: to be clear, this is easily solvable by moving the block to a procedure with the key as a parameter. I'm just curious to find out the exact behavior of inline vars.

Edited by havrlisan
Removed double var LPair

Share this post


Link to post
34 minutes ago, havrlisan said:

LKey will always end up with whichever value is in the last loop. Does this mean that declaring a variable inside a loop will only reinitialize it every time at the same address?

This is expected behavior. There is only single address behind inline variable declaration, not multiple ones.

 

Also captured variables are not stored in the stack, but on a heap. Anonymous methods are basically defined as interfaces with a single method - Invoke - implemented by a hidden reference counted class, and captured variables are stored as fields of that class. When an anonymous method is accessed, an instance of that class is constructed behind the scenes, and it is kept alive through reference counting for as long as is required by the anonymous method it wraps. Because you have a loop you will only see the last value in that captured LKey variable because there is only one anonymous method instance created which you are adding to the list again and again with only one captured LKey field.

Edited by Dalija Prasnikar
  • Like 2
  • Thanks 3

Share this post


Link to post
16 hours ago, havrlisan said:

In this code I expected for LKey variable to stay on the stack as it is used in the anonymous procedure below

FYI, the code shown is declaring two separate LPair variables. Get rid of the 1st one, its not being used.

16 hours ago, havrlisan said:

however, LKey will always end up with whichever value is in the last loop. Does this mean that declaring a variable inside a loop will only reinitialize it every time at the same address?

The code shown will not work, because of the way anonymous procedures capture variables not values:

Anonymous Methods in Delphi: Anonymous Methods Variable Binding

16 hours ago, havrlisan said:

Edit: to be clear, this is easily solvable by moving the block to a procedure with the key as a parameter. I'm just curious to find out the exact behavior of inline vars.

Yes, that would be the solution, eg:

procedure AddToList(AKey: string);
begin
  FList.Add(
    procedure
    begin
      FDict.Remove(AKey);
    end);
end;

for var LPair in FDict do
begin
  AddToList(LPair.Key);
end;

 

 

Share this post


Link to post
1 hour ago, Remy Lebeau said:

The code shown will not work, because of the way anonymous procedures capture variables not values:

I think the asker knows this. The question though is how many variables are there for an inline var inside a loop. Is there one variable? Or is the N variables where N is the number of times the loop body executes? 

 

That's the question being asked as I understand it. 

 

The answer is that there is one variable. 

 

Seems like this is the opposite behaviour from C#

 

https://stackoverflow.com/q/271440/505088

 

Personally I feel like Delphi has a poor design choice here. I'd expect a local variable that is defined inside a scope, to have a lifetime that was that scope. 

 

With the C# design you can achieve both possible behaviours by defining the variable inside or outside the loop body. In Delphi you have to resort to putting the body in a separate procedure. 

 

The we have C++ which allow capture by value or variable as specified by the programmer in code. That would also give the same flexibility but without requiring extraction to separate procedure. 

 

Or am I wrong? I'm basing this on reading and not practical experience so I may have misunderstood. 

Edited by David Heffernan
  • Like 3

Share this post


Link to post
11 minutes ago, David Heffernan said:

Or am I wrong? I'm basing this on reading and not practical experience so I may have misunderstood. 

You probably nailed it right.  It would make sense to have each loop iteration create a separate instance of the anonymous method and let it capture the inline variables that are local to its containing loop body.  But as Dalija explained, that is not what happens.  Probably a left-over from when anonymous procedures were introduced and inline variables didn't exist yet, so they just captured variables from the top of the containing function.  Now inline variables need more stack frame management, and anonymous procedures haven't caught up to that reality yet.

  • Like 1

Share this post


Link to post
3 hours ago, Remy Lebeau said:

FYI, the code shown is declaring two separate LPair variables. Get rid of the 1st one, its not being used.

Thanks for your reply, Remy. The code was just a simplified example for demonstration, compiler would not even allow the same variable declaration twice. Come to think of it, such compiler behavior actually does confirm what Dalija said above. 

2 hours ago, David Heffernan said:

The question though is how many variables are there for an inline var inside a loop. Is there one variable? Or is the N variables where N is the number of times the loop body executes? 

This is correct, thanks for clarifying.

2 hours ago, David Heffernan said:

The answer is that there is one variable. 

 

Seems like this is the opposite behaviour from C#

 

https://stackoverflow.com/q/271440/505088

 

Personally I feel like Delphi has a poor design choice here. I'd expect a local variable that is defined inside a scope, to have a lifetime that was that scope. 

 

With the C# design you can achieve both possible behaviours by defining the variable inside or outside the loop body. In Delphi you have to resort to putting the body in a separate procedure. 

 

The we have C++ which allow capture by value or variable as specified by the programmer in code. That would also give the same flexibility but without requiring extraction to separate procedure. 

I did not know this behavior existed in C#, but it is exactly what I wanted to achieve here. Unfortunately, I don't think we'll see such behavior anytime soon in Delphi.

Share this post


Link to post
4 hours ago, David Heffernan said:

The we have C++ which allow capture by value or variable as specified by the programmer in code. That would also give the same flexibility but without requiring extraction to separate procedure.

This kind of behavior would be the best.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×