havrlisan 24 Posted May 25, 2023 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
Minox 7 Posted May 25, 2023 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
havrlisan 24 Posted May 25, 2023 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
programmerdelphi2k 237 Posted May 25, 2023 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)! 1 2 Share this post Link to post
havrlisan 24 Posted May 25, 2023 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)! Very good explanation, thank you! Share this post Link to post
havrlisan 24 Posted October 9, 2023 (edited) 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 October 10, 2023 by havrlisan Removed double var LPair Share this post Link to post
Dalija Prasnikar 1396 Posted October 9, 2023 (edited) 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 October 9, 2023 by Dalija Prasnikar 2 2 Share this post Link to post
Remy Lebeau 1398 Posted October 10, 2023 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
David Heffernan 2345 Posted October 10, 2023 (edited) 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 October 10, 2023 by David Heffernan 3 Share this post Link to post
Remy Lebeau 1398 Posted October 10, 2023 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. 1 Share this post Link to post
havrlisan 24 Posted October 10, 2023 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
Dalija Prasnikar 1396 Posted October 10, 2023 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