6

just trying to achieve similar functionality of C/C++ static local variables in ObjectPascal/Delphi. Let's have a following function in C:

bool update_position(int x, int y)
{
    static int dx = pow(-1.0, rand() % 2);
    static int dy = pow(-1.0, rand() % 2);

    if (x+dx < 0 || x+dx > 640)
        dx = -dx;
    ...
    move_object(x+dx, y+dy);
    ...
}

Equivalent ObjectPascal code using typed constants as a static variable replacement fails to compile:

function UpdatePosition(x,y: Integer): Boolean;
const
  dx: Integer = Trunc( Power(-1, Random(2)) );  // error E2026
  dy: Integer = Trunc( Power(-1, Random(2)) );
begin
  if (x+dx < 0) or (x+dx > 640) then
    dx := -dx;
  ...
  MoveObject(x+dx, y+dy);
  ...
end;

[DCC Error] test_f.pas(332): E2026 Constant expression expected

So is there some way for a one-time pass initialized local variable ?

David Unric
  • 7,421
  • 1
  • 37
  • 65
  • A more sensible option might be to give your function its own unit, and declare the global in that unit's implementation var section. It is then private to that unit, and thus, to that function. I have a hard time even thinking of a time when I would want to do this, rather than use an object with a private field. – Warren P Dec 11 '11 at 13:35

2 Answers2

6

There's no direct equivalent of a C static variable in Delphi.

A writeable typed constant (see user1092187's answer) is almost equivalent. It has the same scoping and instancing properties, but does not allow the one-time initialization that is possible with a C or C++ static variable. In any case it is my opinion that writeable typed constants should be shunned as a quaint historical footnote.

You can use a global variable.

var
  dx: Integer;
  dy: Integer 

function UpdatePosition(x,y: Integer): Boolean;
begin
  if (x+dx < 0) or (x+dx > 640) then
    dx := -dx;
  ...
  MoveObject(x+dx, y+dy);
  ...
end;

You have to do the one-time initialization in the initialization section:

initialization
  dx := Trunc( Power(-1, Random(2)) );
  dy := Trunc( Power(-1, Random(2)) );

Of course this make a mess of the global namespace unlike the limited scope of a C static variable. In modern Delphi you can wrap it all up in a class and use a combination of class methods, class vars, class constructors to avoid polluting the global namespace.

type
  TPosition = class
  private class var
    dx: Integer;
    dy: Integer;
  private
    class constructor Create;
  public
    class function UpdatePosition(x,y: Integer): Boolean; static;
  end;

class constructor TPosition.Create;
begin
  dx := Trunc( Power(-1, Random(2)) );
  dy := Trunc( Power(-1, Random(2)) );
end;

class function TPosition.UpdatePosition(x,y: Integer): Boolean;
begin
  // your code
end;
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thx for the answer, David. I did emphasized to use local variable to avoid global space as you mentioned after all. The app should be thread-safe written in mind. Probably going to use class-wrapping workaround. – David Unric Dec 11 '11 at 11:47
  • C statics are not threadsafe as I'm sure you know. And class-wrapping approach won't be threadsafe either. You'll need some form of locking or lock-free coding. – David Heffernan Dec 11 '11 at 11:50
  • what's the difference between `class constructor TPosition.Create` and 'classic' `constructor TPosition.Create` ? – Martin Melka Dec 11 '11 at 12:07
  • @Magicmaster The class constructor is called once only, during the unit's initialization. Similarly the class destructor is called once only during the finalization. I included a link to the documentation which gives more details. – David Heffernan Dec 11 '11 at 12:08
  • @Magicmaster Also it is a class method and so can only access class variables and call other class methods. They were introduced to allow related code to be more clearly grouped together. Code in initialization section is often removed from the code that it affects. – David Heffernan Dec 11 '11 at 13:59
4

Enable "Writable typed constants":

{$J+}
procedure abc;
const
  II: Integer = 45;

begin
  Inc(II);
  ShowMessage(IntToStr(II));
end;
{$J-}

procedure TForm1.Button1Click(Sender: TObject);
begin
  abc;
  abc;
  abc;
end;
kludg
  • 27,213
  • 5
  • 67
  • 118
  • This does indeed work but rather than doing project wide compiler options, the [`{$WRITEABLECONST}`](http://docwiki.embarcadero.com/RADStudio/en/Writeable_typed_constants_(Delphi)) directive would always be preferred. All the same, I'm a bit skeptical of this option since it makes all constants static and writeable while it is active. – David Heffernan Dec 11 '11 at 12:13
  • 1
    What cannot be done with a writeable typed constant is the type of initialization performed in the code in the question. – David Heffernan Dec 11 '11 at 12:28