views:

467

answers:

7

Consider:

const 
   clHotlight: TColor = $00FF9933;
   clLink = clHotLight; //alias of clHotlight

[Error] file.pas: Constant expression expected

and the alternate wording that works:

const 
   clHotlight = TColor($00FF9933);
   clLink = clHotLight; //alias of clHotlight

Explain.


Then consider:

const 
   AdministratorGUID: TGUID = '{DE44EEA0-6712-11D4-ADD4-0006295717DA}';
   SuperuserGUID = AdministratorGUID; //alias of AdministratorGUID

[Error] file.pas: Constant expression expected

And fix.

Edit: Added keyword const before declarations; someone didn't believe they were const.

+4  A: 
clHotlight: TColor = $00FF9933; 
          ^

Is declaring clHotlight as a 'variable' (well, ok an 'assignable constant' if you've permitted this in compiler options) by means of the :.

As you've found, declaring:

clHotlight = TColor($00FF9933); 

Makes no allocation of clHotlight until it is specified later.

The same applies to your GUID.

Brian Frost
But i don't have the "Assignable typed constants" option checked! :(
Ian Boyd
That doesn't matter, Ian. The option only controls whether they're assignable. It doesn't control the way they're stored.
Rob Kennedy
+4  A: 

Read here Understanding Typed Constants in Delphi

Mohammed Nasman
-1 for a link without a summary. Besides, that article doesn't even address the issue being asked about in the question.
Rob Kennedy
+4  A: 

I tried this code :

  const
    CAnswer1 = 42;
    CAnswer2 : Integer = 42;

  var
    LAnswer : Integer;

  begin
    LAnswer := CAnswer1;
    LAnswer := CAnswer2;
  end;

and here is the produced code :

Project9.dpr.18: LAnswer := CAnswer1;
004101AC C7056C6E41002A00 mov [$00416e6c],$0000002a //<- assign a hard-coded "42" value
Project9.dpr.19: LAnswer := CAnswer2;
004101B6 A1701C4100       mov eax,[$00411c70] //<- fetch a variable's content
004101BB A36C6E4100       mov [$00416e6c],eax //<- assign this content 

You are right : some constants are more constant than others. The second constant is actually treated by the compiler as a variable.

LeGEC
+1 for a good amount of work, and proof-of-concept explanation
Ian Boyd
+8  A: 

clHotlight: TColor = $00FF9933; is not a constant but a typed constant (=static variable), i.e. the compiler reserves a slot in memory for a TColor which will hold the value $00FF9933 initially at run time.
Because that value can be changed later (with the Assignable Const option ON), it is not a real constant and cannot be accepted by the compiler in clLink = clHotLight;

clHotlight = TColor($00FF9933); is strictly the same as clHotlight = $00FF9933;
It is a true constant and the compiler will replace clHotlight by its value $00FF9933 wherever it appears in the code. And for clLink as well.

Read on this SO question (In Delphi 7, why can I assign a value to a const?) and all the good answers there...

EDIT: about TGUID...
The problem is that writing AdministratorGUID: TGUID = '{DE44EEA0-6712-11D4-ADD4-0006295717DA}'; is not proper.
It is using some compiler magic to call StringToGUID behind the scene, allowing the convenience to express the GUID as a string which they are not by nature. They are records.

So, trying AdministratorGUID = '{DE44EEA0-6712-11D4-ADD4-0006295717DA}'; will not work. That is not a GUID...

A workaround is to have a typed constant and variables pointing to the same memory area using the absolute directive:

const
   AdministratorGUID: TGUID = '{DE44EEA0-6712-11D4-ADD4-0006295717DA}';
var
   SuperuserGUID: TGUID absolute AdministratorGUID; //alias of AdministratorGUID
   RootGUID: TGUID absolute AdministratorGUID;      //alias of AdministratorGUID
François
i see what you, and the other SO question, is saying. But what syntax can i use for the TGUID to make the desired syntax work, e.g. `SuperuserGUID = AdministratorGUID; RootGUID = AdministratorGUID;' (i.e. where the constant is a record) Or is it not possible in Delphi?
Ian Boyd
See my Edit in the answer above.
François
A: 

welcome to Delphi evolution. in delphi 1 & 2, you can not assign initial constant value to a global var (ex: var xVar: Integer = 1). The only way you can do that is using const xVar: Integer = 1) and some where in you codes, you can then change it to somethingelse if desired. Until they get rid of this ancient feature, you can not using "const xVar: Integer" construct as a const value.

Cheers A Pham

APZ28
+3  A: 

The problem arises because a typed constant is not, truly, a constant, as has been explained with varying degrees of clarity and success by others.

What hasn't as yet been shown is how to work around the problem (in a large number of cases), though a couple came tantalisingly close to giving up that secret... :)

In your specific case you can get around the problem by reversing the "aliasing" of the value and the typed constant declaration as follows:

const
  clLink = $00FF9933;
  clHotlight: TColor = clLink;

clLink now provides your true constant and clHotlight is the typed constant that has the same value as clLink.

For the GUID's the same technique can be used, but you have to bear in mind the normal constant expression used to initialise a typed GUID constant - it doesn't use a record but a simple literal string, so:

const
  ID_CONSTANT = '{AA1C8AF2-C290-40AB-9CF5-2888A46E1660}';
  GUID_CONSTANT: TGUID = ID_CONSTANT;

NOTE: Such GUID constants are perfectly usable in all places where TGUID's are required, e.g. IsEqualGUID( tguid, GUID_CONSTANT ) etc.

Deltics
Unfortunately, it muddies the GUID declaration because of this inconsistency: ID_CONSTANT is a string and not a TGUID in spite of a declaration which seems the same as the typed const declaration of a TGUID.
François
I'm not sure where/why/how it is made "muddy". I can't off-hand think of an occasion where a true "constant expression" GUID value would be required/useful. The usual place where this crops up is in a "case" statement, and you couldn't "case" on a GUID value even if it was possible to declare true constant records.
Deltics
We use constant GUIDs all the time. The most useful is `NULL_GUID: TGUID = '{00000000-0000-0000-0000-000000000000}'; if IsEqualGUID(key, NULL_GUID) then`. Another example is the one you saw, checking if the thing is a particular predefined user. Another example is declaring a constant COM **clsid** , e.g.: `CLASS_UIRibbonFramework: TGUID = '{926749FA-2615-4987-8845-C33E65F2B957}';`
Ian Boyd
I could be wrong but don't think these are examples of cases where a CONSTANT EXPRESSION is required (the subset of all "constant" declarations that are considered "true" literals). i.e. you can happily use TYPED constants in such places: NULL_GUIDSTR = '{00000000-0000-0000-0000-000000000000}'; NULL_GUID: TGUID = NULL_GUIDSTR; if IsEqualGUID(key, NULL_GUID) compiles perfectly.
Deltics
A: 

The right side of a constant declaration must be a "constant expression", which is defined as "a constant expression is an expression that the compiler can evaluate without executing the program in which it occurs". You can find the whole accepted syntax for constant expression in the language guide. Note that the language guide explicitly states "Typed constants cannot occur in constant expressions." - and that's why your declarations fails, both clHotlight: TColor = $00FF9933; and AdministratorGUID: TGUID =...; are typed constants. Also, constant expression cannot include functions calls except those listed in the language guide (i.e. Length(), SizeOf(), and some others) that the compiler is able to compute at compile time. Rewrite this way:

const
  AdminGUID = '{DE44EEA0-6712-11D4-ADD4-0006295717DA}';
  AdministratorGUID: TGUID = AdminGUID;
  SuperuserGUID: TGUID = AdminGUID;

And it will work.

ldsandon