In the Delphi code I am looking at I've found the following set of lines:
const
function1: function(const S: String): String = SomeVariable1;
function2: function(const S: String): String = SomeVariable2;
What is this doing? I mean, not the actual code within the functions, but what does it do to declare a function inside the const section and compare(?) it with a variable value? I'm assuming the single equals is a comparison since that's what it is everywhere else in Delphi.
Thank you.
No, the equals is an assignment, as this is how constants are assigned. Consider, for example,
const Pi = 3.1415;
or
const s = 'This is an example';
There are also 'typed constants':
const Pi: extended = 3.1415;
In your snippet above, we define a typed constant that holds a function of signature function(const S: String): String. And we assign the (compatible) function SomeVariable1 to it.
SomVariable1 has to be defined earlier in the code, for instance, as
function SomeVariable1(const S: String): String;
begin
result := S + '!';
end;
Consider the following example:
function SomeVariable1(const S: String): String;
begin
result := S + '!';
end;
const
function1: function(const S: String): String = SomeVariable1;
procedure TForm1.FormCreate(Sender: TObject);
begin
caption := function1('test');
end;
Andreas's answer covers the technical bits very well, but I'd like to provide an answer to this part:
What is this doing?
More along the lines of Why use this weired-looking construct? I can think of two reasons:
The code is written with {$J+} (assignable typed constants), and the "constant" is assigned a different value at some point. If function1 were declared as a variable, the initialization would need to be done in the initialization section of the unit, and that might be too late (if some other unit's initialization section runs before this one and attempts calling the function1 "function")
Used if the function name was changed from function1 to SomeVariable1 and there's 3rd party code that can't easily be changed. This provides a one-line way of declaring the alias.
Related
In an assignment, how can I distinguish between assigning the return value of a function that takes no parameters (and therefore the call may have no parens), and assigning the function reference itself. Is it only by looking at the type of the receiving variable?
Here is a code fragment for illustration (Delphi noob here):
type
TIntFun = function():integer;
var
IntFun : TIntFun;
I : integer;
function AnIntFun(): integer;
begin
result := 3;
end;
begin
I := AnIntFun; // argumentless call, returning 3
IntFun := AnIntFun; // function assignment?
end
Is it only by looking at the type of the receiving variable?
Yes.
This is, in my view, a strong argument against allowing the function call syntax to omit parens. If that language feature did not exist, then you would have to write:
I := AnIntFun();
IntFun := AnIntFun;
If you were forced to write it this way then the compiler would regard
I := AnIntFun;
as a syntax error. The expression AnIntFun would always be of procedural type and the compiler (and the reader) would not need to rely on context to determine the type of the expression.
Relying on context to determine the type of the expression is very undesirable. Consider for instance function overloading:
procedure foo(fun: TIntFun); overload;
procedure foo(val: Integer); overload;
When you write:
foo(AnIntFun);
which overload do you think is chosen? If the language required parens on every function call then AnIntFun is always of procedural type, and the first overload would be chosen without ambiguity.
Your code will work as expected.
But to be explicit, you can write AnIntFun() if you want to execute the function and #AnIntFun if you mean the function itself.
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TIntFcn = function: Integer;
procedure Test(Val: Integer); overload;
begin
Writeln('Value: ' + Val.ToString);
end;
procedure Test(Fcn: TIntFcn); overload;
begin
Writeln('Function. Returned value: ' + Fcn.ToString);
end;
function TestFcn: Integer;
begin
Result := 3;
end;
begin
Test(TestFcn()); // value (obvious)
Test(#TestFcn); // fcn (obvious)
Test(TestFcn); // value
Readln;
end.
What difference does it make when I use a const parameter in a procedure?
Take the following procedure for example:
procedure DoSomething(Sender: TObject; const Text: String; var Reply: String);
begin
//Text is read-only and Reply will be passed back wherever DoSomething() was called
Reply:= Text;
end;
The parameter Text: String is prefixed with const so that (as far as I know), a copy of the value is made and used - and is read-only. What I was wondering is how is does this affect the application any differently than if I didn't put const there? Perhaps a performance trick?
Looking at the documentation states:
"Using const allows the compiler to optimize code for structured - and string-type parameters. It also provides a safeguard against unintentionally passing a parameter by reference to another routine."
In case of a string for example the optimization means there is no additional refcounting when passing as const. Also passing as const does not mean it's a copy. Often it internally passes as reference because the compiler ensures no write access to it.
Some very interesting articles to completly understand what's going on under the hood:
http://delphitools.info/2010/07/28/all-hail-the-const-parameters
http://vcldeveloper.com/articles/different-function-parameter-modifiers-in-delphi
Edit:
A simple example to show that const may result in pass by reference internally:
program Project1;
{$APPTYPE CONSOLE}
type
PMyRecord = ^TMyRecord;
TMyRecord = record
Value1: Cardinal;
Value2: Cardinal;
end;
procedure PassAsConst(const r: TMyRecord);
begin
PMyRecord(#r).Value1 := 3333;
PMyRecord(#r).Value2 := 4444;
end;
procedure PassByVal(r: TMyRecord);
begin
PMyRecord(#r).Value1 := 3333;
PMyRecord(#r).Value2 := 4444;
end;
var
r: TMyRecord;
begin
r.Value1 := 1111;
r.Value2 := 2222;
PassByVal(r);
Writeln(r.Value1);
Writeln(r.Value2);
PassAsConst(r);
Writeln(r.Value1);
Writeln(r.Value2);
Readln;
end.
When you don't have the const prefix, the compiler has to assume that you will be changing the parameter. That means copying it and setting up a hidden try...finally to dispose of the local string variable, so sometimes the const can yield a significant performance improvement. It also makes the generated code smaller.
In addition to the previous answers of efficiency when using a const (i.e. the compiler does not need to copy the variable), if you use a const with an Interface parameter, it prevents the triggering of ref counting.
When you use const string parameter in your function it is your promise to Delphi compiler that you would not call any other function from it, at least not before you made copies of all those const string parameters into local variables you would have for them.
https://github.com/the-Arioch/XE2_AutoOpenUnit/blob/master/Delphi_String_Bug/Girli_str_2xFree_Minimized.dpr
https://plus.google.com/+AriochThe/posts/WB3toSpAdfA
program Girli_str_2xFree_Minimized;
{$APPTYPE CONSOLE}
// initial mini-demo by Ãèðëèîíàéëüäî - http://www.sql.ru/forum/memberinfo.aspx?mid=249076
// variance IfDef's re-added by Arioch (orginal reporter)
uses
SysUtils;
{.$Define TestBug_Impl_Exit}
{.$Define TestBug_Impl_AsIs}
{.$Define NewStr_Unique}
var
rFile: string;
// global or local does not matter.
// originally it was an object member.
{$IfNDef TestBug_Impl_Exit} {$IfNDef TestBug_Impl_AsIs}
function TestBUG(const S: string): string;
begin
Result := S;
end;
{$EndIf}{$EndIf}
{$IfDef TestBug_Impl_AsIs}
procedure TestBUG(const S: string; var Result: string);
begin
Result := S;
end;
{$EndIf}
{$IfDef TestBug_Impl_Exit}
function TestBUG(const S: string): string;
begin
Exit(S);
end;
{$EndIf}
procedure Test(const FileName: string);
{$IfDef TestBug_Impl_AsIs} var unnamed_temp: string; {$EndIf}
begin
// rFile := FileName.SubString(0, Length(FileName)); // unavail in XE2
{$IfNDef NewStr_Unique}
rFile := Copy(FileName, 1, Length(FileName));
// reference-counting broken, de facto writes into const-string (destroys it)
{$Else}
rFile := FileName; // no bug, reference-counting proceeded normally!
UniqueString(rFile);
{$EndIf}
{$IfNDef TestBug_Impl_AsIs}
TestBUG(FileName); // try to use the const-pointer to the old string
{$Else}
TestBUG(FileName, unnamed_temp);
{$EndIf}
end; // <== Fatality here
begin
try
try
rFile := ParamStr(0);
Test(rFile);
Writeln('Safely returned from the hazardous function without memory dislocations.');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
finally
Writeln;
Writeln('Read the output. Press ENTER to terminate the program.');
Readln;
end;
end.
Since volatile string and const string are de facto two different types - the classic compiler treats them differently - there should had been data conversion added: when calling const-string function passing it volatile-string parameters Delphi could had increment the use counter and decrease it after function exit. Passing const-strings parameters down the line into next const-string function would be just how it is now, once compiler typecasted volatile string into const string it can remain so.
Sadly, it does not. And here be dragons.
So, if your function has const string parameters - either do not call from it or cache those parameters into local vars.
Arrays can be indexed using user-defined enumerated types. For example:
type
TIndexValue = (ZERO = 0, ONE, TWO, THREE, FOUR);
var
MyArray: array[Low(TIndexValue) .. High(TIndexValue)] of String;
Elements from this array can then be referenced using TIndexValue values as an index:
MyArray[ZERO] := 'abc';
I am trying to obtain this same general functionality with a TStringList.
One simple solution is to cast every index value to an Integer type at the time of reference:
MyStringList[Integer(ZERO)] := 'abc';
Another solution (to hide all the casting) is to create a subclass of TStringList and defer all the casting to this subclass's subroutines that access the inherited Strings property:
type
TIndexValue = (ZERO = 0, ONE, TWO, THREE, FOUR);
type
TEIStringList = class(TStringList)
private
function GetString(ItemIndex: TIndexValue): String;
procedure SetString(ItemIndex: TIndexValue; ItemValue: String);
public
property Strings[ItemIndex: TIndexValue]: String
read GetString write SetString; default;
end;
function TEIStringList.GetString(ItemIndex: TIndexValue): String;
begin
Result := inherited Strings[Integer(ItemIndex)];
end;
procedure TEIStringList.SetString(ItemIndex: TIndexValue; ItemValue: String);
begin
inherited Strings[Integer(ItemIndex)] := ItemValue;
end;
This works fine for a single implementation that uses the enumerated type TIndexValue.
However, I would like to re-use this same logic or subclass for several different TStringList objects that are indexed by different enumerated types, without having to define TStringList subclasses for each possible enumerated type.
Is something like this possible? I suspect I may have to depend on Delphi's Generics, but I would be very interested to learn that there are simpler ways to achieve this.
I think that generics would be by far the most elegant solution. Using them would be as simple as rewriting your class above as:
TEIStringList<T> = class(TStringList)
and then replacing all TIndexValue references with T. Then you could create it just as any other generic:
var
SL: TEIStringList<TIndexValue>;
begin
SL:=TEIStringList<TIndexValue>.Create;
(...)
ShowMessage(SL[ZERO])
(...)
end;
If you insist on avoiding generics, maybe operator overloading would be of use. Something like the following should work:
type
TIndexValueHolder = record
Value : TIndexValue;
class operator Implicit(A: TMyRecord): integer;
end;
(...)
class operator TIndexValueHolder.Implicit(A: TMyRecord): integer;
begin
Result:=Integer(A);
end;
Then use with:
var
Inx : TIndexValueHolder;
begin
Inx.Value:=ZERO;
ShowMessage(SL[Inx]);
end
UPDATE:
You could adapt TIndexValueHolder for use in a for or while loop by adding Next, HasNext, etc. methods. This might end defeating the purpose, though. I'm still not sure what the purpose is, or why this would be useful, but here's some ideas for how to do it, anyways.
You probably can use a class helper and declare the default property index as Variant:
type
TEnum1 = (Zero = 0, One, Two, Three, Four);
TEnum2 = (Nul = 0, Een, Twee, Drie, Vier);
TEnum3 = (Gds = 0, Psajs, Oeroifd, Vsops, Wowid);
TStringListHelper = class helper for TStringList
private
function GetString(Index: Variant): String;
procedure SetString(Index: Variant; const Value: String);
public
property Strings[Index: Variant]: String read GetString write SetString;
default;
end;
function TStringListHelper.GetString(Index: Variant): String;
begin
Result := inherited Strings[Index];
end;
procedure TStringListHelper.SetString(Index: Variant; const Value: String);
begin
inherited Strings[Index] := Value;
end;
Testing code:
procedure TForm1.Button1Click(Sender: TObject);
var
Strings: TStringList;
begin
Strings := TStringList.Create;
try
Strings.Add('Line 1');
Strings.Add('Second line');
Strings[Zero] := 'First line';
Memo1.Lines.Assign(Strings);
Caption := Strings[Psajs];
finally
Strings.Free;
end;
end;
See edit history for a previous less successful attempt.
If I am trying to call a procedure which has a record type (not object) as a parameter, is it possible to somehow pass details of that parameter "inline" without having to declare a variable of that type first?
eg assume I have this simple record type:
type TMyRecord = record
AString: string;
AnInt: Integer;
end;
and this procedure declaration:
procedure MyProcedure(Rec: TMyRecord);
If I want to call MyProcedure do I have to declare a variable of type TMyRecord or can I do something like:
MyProcedure(TMyRecord("Test", 10));
That doesn't work (XE2) (get a compiler error about it expecting a ")").
So, can I do something like that? Or not possible.
Thanks
It is possible using the advanced record structure.
For more information about advanced records, see the Records (advanced) section in Delphi help.
This is a small prototype to see how it works in your case to preinitialize a record in a function/procedure call :
Type
TRecord = record
AString : String;
AnInt : Integer;
Constructor Create( Const s : String; i : Integer);
end;
constructor TRecord.Create(const s: String; i: Integer);
begin
AString := s;
AnInt := i;
end;
procedure DoSomething( theRec : TRecord);
begin
WriteLn(theRec.AString, ' ',theRec.AnInt);
end;
begin
DoSomeThing( TRecord.Create('S',1));
ReadLn;
end.
Looking at the Delphi RTL, see the definitions of the record types TPoint and TRect in unit system.types (XE2).
They define some overloaded Create constructors, which are used in lots of places to preinitialize the record structures in function/procedure calls.
The question you are asking relates to code readability and there is a solution that avoids having to create a variable. The VCL uses this solution with the records TPoint and TRect.
Consider the definition of TPoint:
type
TPoint = record
X,Y integer
end;
To pass a TPoint to a procedure you might do:
var
MyPoint : TPoint;
begin
MyPoint.X := 5;
MyPoint.Y := 7;
DoSomething( MyPoint );
end;
This is fine but takes 3 lines when one is also possible using the factory function Point:
begin
DoSomething( Point(5,7) );
end;
In Delphi, a function has been declared as follows:
function Point( X, Y : integer ) : TPoint;
begin
Result.X := X;
Result.Y := Y;
end;
You can then call this function 'inline' to create the record 'on the fly' to to quickly
You will see the same has been provided for TRect etc. I often put such a factory function together with the record declaration as follows, even if I don't plan to use them yet:
type
TMyRecord = record
A : integer;
B : string;
end;
function MyRecord( A : integer; const B : string ) : TMyRecord;
begin
Result.A := A;
Result.B := B;
end;
Use of this technique can improved the readability of code and also ensures that you don't accidently omit setting a record element.
Just having fun with John Easley's idea:
type TRec = record
X: string;
Y: Integer;
end;
procedure TestRec(const Rec: array of const);
var
R: TRec;
begin
R.X:= string(Rec[0].VUnicodeString);
R.Y:= Rec[1].VInteger;
ShowMessage(R.X + IntToStr(R.Y));
end;
procedure TForm1.Button7Click(Sender: TObject);
begin
TestRec(['Test', 22]);
end;
It is possible to pass record fields as array of const parameters and assign these parameters to local record variable.
It would be nice! But, no.
If passing things inline is really your objective, then perhaps Open Array Parameters would suit you.
Procedure MyProcedure(const Vars: Array of Variant);
begin
ShowMessage(VarToStr(Vars[0])+' '+VarToStr(Vars[1]));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
MyProcedure(['Test', 12]);
end;
You could also pass an Array of Const, which is basically an array of TVarRec which is a variant record that also includes type information as VType. This is fun stuff..
An excellent article can be found on Rudy's Delphi Corner here:
Rudy's Delphi Corner, Open Array Parameters
I'm trying to write a generic cached property accessor like the following but am getting a compiler error when trying to check whether the storage variable already contains a value:
function TMyClass.GetProp<T>(var ADataValue: T; const ARetriever: TFunc<T>): T;
begin
if ADataValue = Default(T) then // <-- compiler error on this line
ADataValue := ARetriever();
Result := ADataValue;
end;
The error I'm getting is "E2015 Operator not applicable to this operand type".
Would I have to put a constraint on T to make this work? The help file says that Default() would accept anything except generic types. In my case I'm dealing mostly with simple types like String, Integer and TDateTime.
Or is there some other library function to perform this particular check?
I'm using Delphi 2009 in case that matters.
P.S.: Just in case it isn't clear from the code what I'm trying to do: In my case determining the actual property values might take a while for various reasons and sometimes I might not even need them at all. On the plus side however the values are constant so I only want to call the code that determines the actual value the first time that property is accessed and then store the value in a class field and the next time that property is accessed return the cached value directly. Here's an example of how I hoped I would be able to use that code:
type
TMyClass = class
private
FSomeProp: String;
function GetSomeProp: String;
function GetProp<T>(var ADataValue: T; const ARetriever: TFunc<T>): T;
public
property SomeProp read GetSomeProp;
end;
function GetSomeProp: String;
begin
Result := GetProp<String>(FSomeProp,
function: String
begin
Result := SomeSlowOrExpensiveCalculation;
end);
end;
(obviously, there's more than just one property)
After a hint in the comments from Binis and digging around a little in Generics.Collections I came up with the following which appears to work just as I wanted it:
function TMyClass.GetProp<T>(var ADataValue: T; const ARetriever: TFunc<T>): T;
var
lComparer: IEqualityComparer<T>;
begin
lComparer := TEqualityComparer<T>.Default;
if lComparer.Equals(ADataValue, Default(T)) then
ADataValue := ARetriever();
Result := ADataValue;
end;
The problem is not the Default function, but the equality operator =.
You could constrain T to IEquatable and use the Equals method like this:
TMyClass = class
function GetProp<T : IEquatable<T>>(var ADataValue: T; const ARetriever:
end;
...
function TMyClass.GetProp<T>(var ADataValue: T; const ARetriever: TFunc<T>): T;
begin
if ADataValue.Equals (Default(T)) then
ADataValue := ARetriever();
Result := ADataValue;
end;