11

Don't shoot me, but it's the first time I saw a usage of a local writable const (Or maybe I'm just way too senile...): "The WinAPI way (by Peter Below from TeamB)"

Take a look at the local const FullScreen: Boolean = False; and then FullScreen := not FullScreen;

At first I thoght It was a new feature with modern Delphi versions, but it works with my D5 also. So my question is: are local writable constants are exactly the same as declaring a global writable constant?

e.g.

procedure TForm1.Button1Click(Sender: TObject);
Const
  LocalConst: Boolean = False;
begin
  LocalConst := not LocalConst;
  if LocalConst then Beep;
end;

Works the same like this code? :

Const
  GlobalConst_Button2Click: Boolean = False;

procedure TForm1.Button2Click(Sender: TObject);
begin
  GlobalConst_Button2Click := not GlobalConst_Button2Click;
  if GlobalConst_Button2Click then Beep;
end;

Or, LocalConst is local to it's method i.e. static? Is this constant Thread safe?
Can anyone shed some light on this issue?

Community
  • 1
  • 1
kobik
  • 21,001
  • 4
  • 61
  • 121
  • 6
    @user539484 - stop downvoting anonymously; I'm almost sure it's you! I'm watching your vote cast. – TLama Mar 11 '12 at 12:29

4 Answers4

11

The code with the local and global typed constant does exactly the same thing.

As David already stated the global static variable (aka typed constant) is reachable throughout the program and the local static var is not. Below I will refer to typed constants as static variables because that's what they really are.

However the local static variable does persist in memory in exactly the same way as the global static var does. It is just the compiler that does not allow you access to a local var from outside the routine.
Also note that if your local static var is especially large (or you have very many of them) they will eat up a constant chunk of memory (even though I cannot conceive of a scenario where this could be a problem).

Because the static variable resides in a fixed location it is not thread safe. It effectively turns into a shared variable between all thread instances.
If the static var cannot be altered in a single CPU cycle (i.e. if it's bigger than an integer or if it's a complex type) than two threads can alter different parts of the variable at the same time, often leading to corruption.

It it can be altered in a single cycle, e.g. a boolean or integer than you can never know whether the thread you are in is the one that changed it last or another one did which will lead to unpredictable results in most cases.

In short
Using static vars in threaded code is a very bad idea unless you know exactly what you're doing.

An exception to this might be an integer counter where you only increment and test to see if more than x executions have occurred.
Static vars generally unsuitable for passing messages between threads.

If you want to share data between threads it's a better idea to use threadvar,
see: http://en.wikipedia.org/wiki/Thread-local_storage
and: https://stackoverflow.com/search?q=delphi+threadvar

Finally
There are very few problems that require global static vars and they are best avoided because they are dangerous.
Local static vars are useful in single threaded code to keep track of state between different executions of a routine.
They are useless for doing this in multi-threaded code because of race conditions.

Community
  • 1
  • 1
Johan
  • 74,508
  • 24
  • 191
  • 319
5

I'm a bit late for the party, but i still would like to add some info on writable constans.

First of all, as Johan and David stated global and local constants do not differ in memory.

For those interested in usage of writable constants: I found it useful to emulate "Promise" functionality to make the function "Lazy". Ofcourse delphi doesn't support Promises, so this is only partially efficient.

Consider a function to count amount of words in a string:

function CountWords(Input: String):Integer;
var
    Worker: TStringList;
begin
  Worker := TStringList.Create;
  Worker.DelimitedText := Input;
  Result := Worker.Count;
  Worker.Free;
end;

Now imagine it beeing called many times in our program. TStringList object will be created and freed each time we do it thus doing extra job. You could ofcourse solve this by creating a global variable Worker_CountWords, initialize it on program start and use it in your function, but take a look at this:

function CountWords(Input: String):Integer;
{$J+} //Enable writable constants
const
    Worker: TStringList = nil;
{$J-} //Disable writable constants
begin
  if Worker = nil then
  begin
    Worker := TStringList.Create;
    //Other Initialization code here
  end;
  Worker.DelimitedText := Input;
  Result := Worker.Count;
end;

This function will only create TStringList once and use it later, but will never free it (kind of a downside here). But for a function that can be called any time during the app is running this is sort of suitable. This can make your code look a bit cleaner if you will... Now, notice - this isn't actually a promise, but it achieves similar results. You could aslo do this with function calls (i've already tried replacing the actual function in memory and it's pretty bad idea, but you could make a const that will hold pointer to function, which at start holds pointer to initialization function and after that replaced to actual worker function and the parent function would only have a call to a function which is held in a constant). I cannot think of a good example right now, so i'll let you figure that one out on your own.

Also it is not required to have {$WRITABLECONST ON} in order to modify constant values, you could also do something like this:

procedure DoSomeWork;
const
  FirstCall : TDateTime = 0;
begin
  if FirstCall = 0 then
    PDateTime(@FirstCall)^ := Now;
  Writeln(TimeToStr(FirstCall));
  //some actual work here
end;

Same thing applies to const parameters in functions, because they are exactly same as var parameters (passed by reference to avoid spending time on creating separate variables), the only difference is that compiler doesn't allow you to change these values normally.

P.S. Be careful with const function parameters, since you can pass actual constants like foo(12) and trying to modify that could probably mess something up...

Rusty
  • 51
  • 1
  • 1
  • How is that `CountWords` function different in functionality to a function using a singleton? – Leonardo Herrera Mar 01 '13 at 14:03
  • I think it is much better to use the {$J approach. It works with the compiler warnings. I would prefer Delphi to have an appropriate keyword like static to replace const / var when static memory is intended. Even compiler directives are a bit messy. – Mark Patterson Jan 16 '14 at 04:30
  • The `PDateTime(@FirstCall)^` trick is very useful. I'm using now in my components. thx – Paulo França Lacerda Jun 11 '19 at 15:59
4

Are local writable constants are exactly the same as declaring a global writable constant?

The only difference is the scope. The global variable is, well, global, and the local variable has local scope. Writeable typed constants are a reasonable approximation to C static local variables.

The enormous disadvantage of writable typed constants is that there is no keyword support like in C and you have to use a compiler option to toggle the meaning of the language! In my view this makes writable typed constants effectively useless.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 10
    Congratulations on reaching 100k rep! I guess that is why you got the flowers! – Andreas Rejbrand Mar 11 '12 at 12:12
  • Too bad the original version of this answer was incorrect. But thanks! – David Heffernan Mar 11 '12 at 12:14
  • @David, Don't forget *who* made ya 100k! :-P Congrats! – kobik Mar 11 '12 at 12:14
  • @kobik According my my reputation audit, it was an upvote in this question: http://stackoverflow.com/questions/9647212/when-linking-statically-does-the-linker-include-the-whole-library/9647235#9647235 – David Heffernan Mar 11 '12 at 12:17
  • "a writeable typed constant is actually a global variable and there is just a single instance of it"... This is what I also thought at first. What made you change your mind and revert the answer? – kobik Mar 11 '12 at 12:17
  • The documentation quote in my original answer was pointless. The statement "a writeable typed constant is actually a global variable and there is just a single instance of it" is valid. – David Heffernan Mar 11 '12 at 12:21
  • There's no stack. It's just a global variable that is only visible from the function. – David Heffernan Mar 11 '12 at 12:36
  • 1
    @David, congrats, but that was just the last one to make it 100k. All other upvotes in the years past were just as necessary. – Rudy Velthuis Mar 11 '12 at 14:34
  • 2
    And congrats from me too! Been watching you get ever closer and had a bet with Sofie (my dog) whether it would be saturday or sunday. :-) – Marjan Venema Mar 11 '12 at 16:49
  • @MarjanVenema Thanks! Did you win the bet or did your dog claim the spoils?! – David Heffernan Mar 11 '12 at 16:53
0

I did not understand the question completely, so i will answer all of the cases that could occur:

  1. If you did not understand how 2 of my CountWords examples differ: First one creates and frees class instance each time it is called. Second one only creates class instance on first call, then uses it till the program terminates.
  2. If you mean how would that differ if you created a singleton and implemented CountWords in it: it wouldn't differ in functionality, but you would have to write a whole new class for it, then initialize it on program start and use it later. Besides there's a lot of criticism on using singletons (see wiki for more info), so i wouldn't do that.
  3. If you are asking about the actual work these two example functions are performing: they obviously yield same results, but first one isn't optimized for running in environment that calls this function a lot. As i said before, you could reach same results by declaring a global variable, but if you do that - you will see your global variable everywhere in your program, whereas it is only needed in one specific location and nowhere else.

P.S. I was actually wrong about const function parameters, it turns out these things behave differently in different delphi versions. Const parameters acted just the same as var parameters in Delphi 6, but in Delphi XE2 it seems local variable is created anyway. Either way i do not recommend messing with const function parameters.

Mike
  • 23,542
  • 14
  • 76
  • 87
Rusty
  • 904
  • 4
  • 10