4

In Delphi prism we can declare variables that is only needed in special occasions.

eg: In prism

 If acondition then
     begin
        var a :Integer;
     end;
    a := 3; //this line will produce error. because a will be created only when the condition is true

Here 'a' cannot be assigned with 3 because it is nested inside a branch. How can we declare a variable which can be used only inside a branch in delphi win32. So i can reduce memory usage as it is only created if a certain condition is true;

If reduced memory usage is not a problem what are the draw backs we have (or we don't have)

Cosmin Prund
  • 25,498
  • 2
  • 60
  • 104
Vibeeshan Mahadeva
  • 7,147
  • 8
  • 52
  • 102
  • 6
    Why? Because those are the rules. – David Heffernan Jun 29 '11 at 10:42
  • 2
    @vibeeshanRC, "memory usage" is not a problem, but +1 because I also want block-level variables in my win32 Delphi code. And about the why: maybe because we're not complaining enough? – Cosmin Prund Jun 29 '11 at 10:48
  • 1
    If you are having memory usage problems, this will most definitly not solve them. – Lieven Keersmaekers Jun 29 '11 at 10:49
  • Yeah, memory usage is not the issue. Encapsulation is the issue. – David Heffernan Jun 29 '11 at 10:52
  • i know it is not going to eat memory if declare one variable; but if we cant do such a thing what is the purpose of having public , private and protected variables. – Vibeeshan Mahadeva Jun 29 '11 at 10:53
  • if you call this 4 lines only once in the program life cycle, then its meaning less, if you have another estimation, of usage, share so we could be more specific on the discussion. – none Jun 29 '11 at 10:58
  • @vibeeshanRC This is a local variable. Public, private etc. are for member variables. – David Heffernan Jun 29 '11 at 11:10
  • 3
    @vibeeshanRC I think probably the majority of Delphi programmers would appreciate more local scope for variables as per C++, C# etc. But we can't do it yet. Maybe one day. Your mention of performance has rather clouded the question because that's not the real issue. – David Heffernan Jun 29 '11 at 11:12
  • That's why i hate Delphi Schism. – Premature Optimization Jun 29 '11 at 11:22
  • I'm sure the `a` variable is reserved in the stack when the function enters, or used as a register. In generated asm code, you don't change the `esp` register on the fly, or put some `push/pop` pairs to have such "sub-local" temporary allocation. Even for a compiler, it becomes a nightmare if an exception is raised: you'll have to put an invisible try...finally block to restore the stack size... not worth it! And even forbidden in x86-64 AFAIK. So such block-level variables are only syntaxic sugar. Delphi compiler is clever enough to use registers instead of a stack variable in such case. – Arnaud Bouchez Jun 29 '11 at 18:25

6 Answers6

8

The concept of Local Variable Declaration Statements like in Java is not supported in Delphi, but you could declare a sub-procedure:

procedure foo(const acondition: boolean);

  procedure subFoo;
  var
    a: integer;
  begin
    a := 3;
  end;

begin
  If acondition then
  begin
    subFoo;
  end;
end;
splash
  • 13,037
  • 1
  • 44
  • 67
  • 4
    How do you mean tooo long? If "a" represents a large memory usage variable such as a: TVeryBigArray surely it's worth the additional overhead? Maybe you should look at other aspects of your code as Lieven commented. – Despatcher Jun 29 '11 at 10:57
  • too long comparing to other languages – Vibeeshan Mahadeva Jun 29 '11 at 11:01
  • @vibeeshanRC, no, it is a downside of some "artist renditions" of the Pascal. Also, there are languages where one can no longer bother about variable declarations at all. – Premature Optimization Jun 29 '11 at 11:27
  • 2
    I realy don't know what you are getting at here, Do you really just want to type less characters? How does moving the declaration help? If it's too long already? Well, sorry but Delphi/Pascal is a bit of a 'wordy' language, it's what makes it easy to maintain and read. It can do clever stuff, just not this particular party trick. – Despatcher Jun 29 '11 at 12:01
  • 2
    +1 for sub procedures, which have names, and introduce readability and clarity into the code. I hope Delphi NEVER supports block scoped variables. – Warren P Jun 29 '11 at 12:45
  • 1
    @Warrent block scoped variables are great. I want to write `for i: Integer in List do` – David Heffernan Jun 29 '11 at 12:49
  • @David If we wanted to write in C variants surely we would learn them... :) I'm with Warren and DSITL I want my variables to stay put where I can see them. Also would these on-the-fly variables not break the single pass compiler model? Where are they to live & die in a situation where an exception occurs after creating it or not? Seems like a lot of compiler work for very little gain. Maybe when Delphi has garbage collection it would be workable. – Despatcher Jun 29 '11 at 13:28
  • 2
    No problem with single pass and local scope. Such a feature would be optional. I give you my word now that I won't force you to use such a new feature. – David Heffernan Jun 29 '11 at 13:32
  • @Despatcher, what's "block level variables" got to do with single pass compiling and garbage collection? They're just local variables, allocated on stack. Probably a lot easier then local sub-routines (because this need to *share* stack). – Cosmin Prund Jun 29 '11 at 15:33
  • @Cosmin, I seem to have been dragged along by the thinking of the OP - he didn't want to create the variable (Memory use originally) if it was not required by the proc. As has been pointed out by RK below the variable would probably be created anyway at entry to the procedure, but if it WASN'T created then how does clean up get implemented correctly under all circumstances? Exit , Exception , Goto? :)) Maybe NOT on the stack afterall - then garbage collection would handle it. 2nd Pass? well just thinking out loud - a habit I must correct as it's usually a disaster:( – Despatcher Jun 29 '11 at 16:02
  • @Despatcher, the problem of ensuring that stack space is deallocated properly in all exit paths has already been solved. It's exactly the same problem that C++ faces when block-level variables go out of scope and their destructors need to run — or, for that matter, that Delphi faces when implementing the `finally` construct. When the compiler enters a new scope, set up a "try" record. In the "finally" block, reset the stack pointer. Easy. – Rob Kennedy Jun 30 '11 at 02:49
  • @Warren, Delphi doesn't have iterators. – Rob Kennedy Jun 30 '11 at 02:51
  • @Cosmin ok, OK. I already knew that, I was just postulating that these types of constructs might cause problems. As another note I don't believe how much traffic this question(?) has created! – Despatcher Jun 30 '11 at 10:09
8

The premise of your question is faulty. You're assuming that in languages where block-level variables are allowed, the program allocates and releases memory for those variable when control enters or leaves those variables' scopes. So, for example, you think that when acondition is true, the program adjusts the stack to make room for the a variable as it enters that block. But you're wrong.

Compilers calculate the maximum space required for all declared variables and temporary variables, and then they reserve that much space upon entry to the function. Allocating that space is as simple as adjusting the stack pointer; the time required usually has nothing to do with the amount of space being reserved. The bottom line is that your idea won't actually save any space.

The real advantage to having block-level variables is that their scopes are limited.

If you really need certain variables to be valid in only one branch of code, then factor that branch out to a separate function and put your variables there.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • And if you're really concerned, make it an inline function. –  Jun 30 '11 at 23:16
  • But where it DOES save memory is if you have (f.ex.) 16 blocks at the same scope level (ie. none are within any of the other), each with a local definition that occupies 1 Mb, then it'll only allocate 1 Mb stack space instead of 16 Mb. Granted - this is a constructed example, but it shows that it _is_ possible to save memory by using scoped declarations... – HeartWare Jun 12 '15 at 14:36
4

There is no way in Delphi to limit scope of an variable to less than entire routine. And in case of a single integer variable it doesn't make sense to worry about it... But in case of large data structure you should allocate it dynamically, not statically, ie instead of

var integers: array[1..10000]of Integer;

use

type TIntArray: array of Integer;
var integers: TIntArray;
If acondition then
 begin
    SetLength(integers, 10000);
    ...
 end;
ain
  • 22,394
  • 3
  • 54
  • 74
  • if you use a single variable its adon of o(1) in space which is meaningless, if you need to use arrays, then this answer solve your question. the process of freeing memory at the end of calling a procedure is o(1) in time, since there is a call back. – none Jun 29 '11 at 10:57
  • thanks ; but i think var integers will be created before going to if condition – Vibeeshan Mahadeva Jun 29 '11 at 10:57
  • 1
    @vibeeshanRC Yes, the variable will be "created" at the beginning of the function, but in case of a dynamic array (or any other dynamic var) only pointer will be created (4 bytes in win32), the memory for elements won't be allocated until you call `SetLength()`. – ain Jun 29 '11 at 11:10
  • @vibeeshanRC the fixed length array version of integers lives at a fixed location in the functions stack frame, there's no allocation. – David Heffernan Jun 29 '11 at 11:11
  • 1
    @ain If the array can safely fit in the stack, and I know how big it needs to be, then I would always prefer the stack to the heap for performance reasons. – David Heffernan Jun 29 '11 at 11:13
  • @David Heffernan The OP is worried about memory consumption, not about perfomance. – ain Jun 29 '11 at 11:22
  • @ain Local scope doesn't necessarily result in reduced stack consumption. Depends on the compiler. A compiler could re-use stack locations even without local scope. Good compilers do. It's very easy for the compiler to analyse the code and work out where stack can be re-used. – David Heffernan Jun 29 '11 at 11:31
  • @David Delphi doesn't reuse stack location AFAIK, but can use registers for such local variables, if no sub method or function is called. You're perfectly right: this is compiler job to optimized at this level. All this is syntaxic sugar. – Arnaud Bouchez Jun 29 '11 at 18:30
  • @A.Bouchez On the other side of the coin, there's no guarantee that, just because a language supports locally scoped variables within a method, the compiler will emit code that leads to stack re-use. As you say, all just sugar. – David Heffernan Jun 29 '11 at 18:44
3

You can emulate block-level variables with the (dreaded) with statement plus a function returning a record. Here's a bit of sample code, written in the browser:

type TIntegerA = record
  A: Integer;
end;

function varAInteger: TIntegerA;
begin
  Result.A := 0;
end;

// Code using this pseudo-local-variable
if Condition then
  with varAInteger do
  begin
    A := 7; // Works.
  end
else
  begin
    A := 3; // Error, the compiler doesn't know who A is
  end;

Edit to clarify this proposition

Please note this kind of wizardry is no actual replacement for true block-level variables: Even those they're likely allocated on stack, just like most other local variables, the compiler is not geared to treat them as such. It's not going to do the same optimizations: a returned record will always be stored in an actual memory location, while a true local variable might be associated with a CPU register. The compiler will also not let you use such variables for "for" statements, and that's a big problem.

Cosmin Prund
  • 25,498
  • 2
  • 60
  • 104
  • `a := 3` would definitively not work because the assignment is outside the `with` block and the compiler doesn't know who `a` is. I'm not sure what `Tinteger.a := 3` from your comment means, it's probably a typo. – Cosmin Prund Jun 29 '11 at 11:28
  • `TIntegerA.A := 3` shouldn't even compile because `TIntegerA` is a type name. – Cosmin Prund Jun 29 '11 at 11:34
  • Just now i understood the code it is really tricky; sorry for stating Tinteger.a := 3 will work – Vibeeshan Mahadeva Jun 29 '11 at 11:40
  • Nested `with varAInteger` statement will just compile and you'll have access to the _latest_ record referred, and after the `end` you'll have access to the _previous_ record referred. Such nesting is not very readable, and width is not to be nested at all with the same type, but could be useful with diverse types. – Arnaud Bouchez Jun 29 '11 at 18:17
  • -1 because, from the generated code POV, there is **no interest** of using `with` here. It will reserve every `TIntegerA` on the stack at once, just like a local variable. `with` is just another way of declaring a local `TIntegerA` variable here. You'll rather use a local variable, the heap (via an Interface which will reserve only a pointer storage on the stack but add an hidden try..finally block) or a sub-procedure, or, even better a private method and a OOP design. – Arnaud Bouchez Jun 29 '11 at 18:19
  • @A.Bouchez, about nesting `with` statements, of course that's not very readable, that's why it's the "dreaded" with. About your downvoting comment, I honestly don't understand it: Are you saying that `TIntegerA` being a record it's going to be allocated on the stack? I kind of *expect* local variables to be allocated on stack, so that's not really a problem. And I also don't understand the *no interest* comment. Is that meant to read "it's no different from declaring a local variable of the given type"? – Cosmin Prund Jun 29 '11 at 18:49
  • @Cosmin Yes, the Delphi compiler will always allocate it on the stack, since it's a function result (unless the function is declared `inline`, which is not the case). Even if it would have used a register instead for such an integer variable. The OP is confusing "declared on the stack" and "generated asm code will use the stack". It's up to the compiler to make the decision. So it's different from declaring a local variable, because it will reduce the range of compiler optimization. So the "record function result" trick is not a good answer. – Arnaud Bouchez Jun 29 '11 at 19:39
3

Beware that it could only be "syntactic sugar". The compiler may ensure you don't use the variable outside the inner scope, but that doesn't mean it could save memory. The variable may be allocated on the stack in the procedure entry code anyway, regardless if it is actually used or not. AFAIK most ABI initialize the stack on entry and clean it on exit. Manipulating the stack in a much more complex way while the function is executing including taking care of different execution paths may be even less performant - instead of a single instruction to reserve stack space you need several instruction scattered along code, and ensure the stack is restored correctly adding more, epecially stack unwinding due to an exception may become far more complex. If the aim is to write "better" code because of better scope handling to ensure the wrong variable is not used in the wrong place it could be useful, but if you need it as a way to save memory it could not be the right way.

2

Having commented all that - there is a party trick that Delphi has that has far more uses than a simple local variable and may achieve your aim:

  function Something: Integer;
  begin 
    // don't want any too long local variables...
    If acondition then
      asm
        // now I have lots of 'local' variables available in the registers 
        mov EAX, @AnotherVariable  //you can use pascal local variables too!
        // do something with the number 3
        Add EAX, 3
        mov @Result, EAX
        jmp @next
 @AnotherVariable: dd 10
 @next:
      end;
    end;
  end; 

:)) bit of a pointless example...

Despatcher
  • 1,745
  • 12
  • 18