No overloaded version with these arguments - var param with read/write property - delphi

I have two overloaded procedures, of which I want to call the second one:
function ModifySyncProperties(AEWSItemId: String; AEvent: TcxSchedulerEvent; var AEWSChangeKey: String): Boolean; overload;
function ModifySyncProperties(AEWSItemId: String; ATTID: Integer; ASyncID: String; var AEWSChangeKey: String): Boolean; overload;
This fails with an error, though:
lSuccess := FDMExchange.ModifySyncProperties(lEWSId, lApp.EventID, lNewOutlookID, lApp.EWSItemChangeKey);
There is no overloaded version of 'ModifySyncProperties' that can be called with these arguments
This works, though:
lChangeKey := lApp.EWSItemChangeKey;
lSuccess := FDMExchange.ModifySyncProperties(lEWSId, lApp.EventID, lNewOutlookID, lChangeKey);
lApp.EWSItemChangeKey := lChangeKey;
Here are the types and variables:
lNewOutlookID,
lEWSID,
lChangeKey : String;
lApp : TEWSAppointment;
lSuccess : Boolean;
TEWSAppointment is defined in the interface section of another unit as this:
TEWSAppointment = class
private
FEventID: Integer;
...
FEWSItemChangeKey: String;
...
public
property EventID: Integer read FEventID write FEventID;
...
property EWSItemChangeKey: String read FEWSItemChangeKey write FEWSItemChangeKey;
...
end;
Why does the compiler not accept the read/write lApp property as a var parameter?
I'm using Delphi Rio 10.3.1.

The documentation for var parameters says:
If a routine's declaration specifies a var parameter, you must pass an assignable expression - that is, a variable, typed constant (in the {$J+} state), dereferenced pointer, field, or indexed variable to the routine when you call it.
A property does not meet this requirement.
Further, the documentation for properties calls this out explicitly:
Unlike fields, properties cannot be passed as var parameters, nor can the # operator be applied to a property.
At the implementation level, the ABI, var parameters are implemented by passed the address of the variable. Since a property does not necessarily have a backing variable with an address, the compiler cannot directly obtain such a variable address.
Furthermore, if the property getter or setter perform actions beyond reading or writing to a variable, then they would need to be called.
In principle at least the language could support the usage you wish by declaring a local variable and compiling this code:
localVar := myProperty;
foo(localVar);
myProperty := localVar;
However, the designers of the compiler did not implement this when properties were introduced to the language.

Related

Delphi global variable setter

I got problem with setting global variable in Delphi unit:
unit SomeUnit;
...
interface
...
var
variable1: String;
...
implementation
procedure TSomeForm.SetNewVersion(variable1: String);
begin
variable1 := variable1; //here is problem
end;
How to assign value to global variable if it have same name like local argument value from procedure? If that is some form value, that can be done like this:
TSomeForm.variable1 = variable1;
But problem is when variable is global variable in unit?
SomeUnit.variable1 = variable1; // this dont work
FWIW: The following works as one might expect:
var
SomeForm: TSomeForm;
variable1: string;
implementation
{$R *.dfm}
{ TSomeForm }
procedure TSomeForm.FormCreate(Sender: TObject);
begin
Assert(SomeUnit.variable1 = '');
SetNewVersion('1');
Assert(SomeUnit.variable1 = '1');
end;
procedure TSomeForm.SetNewVersion(variable1: string);
begin
SomeUnit.variable1 := variable1;
end;
To avoid such problems you might consider prefixing arguments with 'A' which is kind of semi-standard in Delphi. And while you're at it, make string parameters const:
procedure TSomeForm.SetNewVersion(const AVariable1: string);
begin
variable1 := AVariable1;
end;
You can solve your problem by either:
Choosing a different name for the parameter (or indeed the global variable). Personally I tend to use the name Value for the parameter of a setter method. Or,
Fully qualifying the name like this SomeUnit.variable1.
Note that the assignment operator is := and not =.
I would strongly recommend that you reconsider the design.
Should the variable really be global? If it is associated with a form instance as is implied by your setter, shouldn't it be a private member variable of the form class.
If the variable really is shared between instances, make the variable a private class variable and the setter a class method.
If your Delphi does not have class variables then move the global variable into the implementation section. As your code stands, there's no point in having a setter because you also expose the backing variable in the interface section.

How to create a type that is string?

A blog entry from Raymond Chen today made me realize the elegant solution to a problem i'm having.
Various shell functions, rather than all taking ITEM­ID­LIST structure, can be made to only accept:
ITEM­ID_CHILD
ID­LIST_RELATIVE
ID­LIST_ABSOLUTE
ITEM­ID_CHILD_ARRAY
structures. The structures are all identical, but now you can enforce conceptual types at the compiler level.
Back to Delphi
i have a set of functions:
some only take a path: (e.g. C:\Users\Ian\Desktop\AllisonAngel.jpg)
some only take a filename: (e.g. AllisonAngel.jpg)
some only take a folder: (e.g. C:\Users\Ian\Desktop)
And right now they're all declared as string, e.g.:
function GetFilenames(Folder: string; ...): ...
function IsValidBatchFilename(Path: string): ...
function GetReportType(Filename: string): ...
and i have to trust that i'm passing the right stuff; and i'm trusting that developers (e.g. me), know the difference between:
a path
a filename
and a folder
i want to change the functions to use "typed" strings:
function GetFilenames(Folder: TFolderOnly; ...): ...
function IsValidBatchFilename(Path: TFullPath): ...
function GetReportType(Filename: TFilenameOnly): ...
Where:
type
TFullPath = type string;
TFolderOnly = type string;
TFilenameOnly = type string;
Except that there's no actual typing happening:
var
dropFolder: string;
begin
dropFolder := GetDropFolderPath(LCT);
GetFilenames(dropFolder); <-- no compile error
end;
What i want is a "distinct" type of string; that is string insofar that it is length prefixed, reference counted, null terminated.
You can use advanced records to accomplish this. For instance, you could do
type
TFileName = record
FFileName: string;
public
class function IsValidFileName(const S: string): boolean; static;
class operator Implicit(const S: string): TFileName;
class operator Implicit(const S: TFileName): string;
end;
implementation
class function TFileName.IsValidFileName(const S: string): boolean;
begin
result := true {TODO};
end;
class operator TFileName.Implicit(const S: string): TFileName;
begin
if IsValidFileName(S) then
result.FFileName := S
else
raise Exception.CreateFmt('Invalid file name: "%s"', [S]);
end;
class operator TFileName.Implicit(const S: TFileName): string;
begin
result := S.FFileName;
end;
and similarly for TPath and TFolder. Advantages:
A function expecting TPath will not accept a TFileName (or some other combination).
You can still assign a TPath to/from a regular string. If you cast from a string to a TPath, you will automatically check the string to see if it contains a valid path.
Optionally, you can specify how a TPath can be assigned to a TFileName (or some other combination), by writing more Implicit class operators.
Create different record types for each string type. Distinct record types are not assignment-compatible, even though string types are.
type
TFullPath = record value: string end;
TFolderOnly = record value: string end;
Chen's article compares the new shell feature to the classic STRICT macro that makes distinct handle types, and as I recall, declaring distinct structs is exactly how the STRICT macro works.
With the way Delphi handles basic types, I'm pretty this is a case of "you can't get there from here."
Your string type declarations are all going to satisfy Delphi's rules for type compatibility and assignment compatibility. What they will restrict is procedural declarations with var parameters. If you changed your function declarations to be var parameters instead of reference or value parameters, you would get a compile error in your final example.
All that said, it's all useless anyway. You still have no way to ensure that the input is only a filename, even with the TFilenameOnly type, and must test the contents in your procedures anyway.

How to overload a function based on the result type?

just a question, i have:
myclass = class
public
function Funct1: String;
function Funct2: Integer;
end;
It turn me error, so i have tried with:
myclass = class
public
function Funct1: String; overload;
function Funct2: Integer; overload;
end;
but same problem; delphi tell me that has same parameter.
Now, i ask, is possible to do in mode to have more function with same name but with different output as in example?
Thanks very much for help.
UPDATE
Sorry, i done a error, not funct1 and funct2, but both funct1, so:
myclass = class
public
function Funct1: String; overload;
function Funct1: Integer; overload;
end;
Doing so, compiler return me this error:
[DCC Error] Project1.dpr(15): E2252 Method 'funct1' with identical parameters already exists
[DCC Error] Project1.dpr(22): E2037 Declaration of 'funct1' differs from previous declaration
Of course, i know becouse give error and need change name to one of both function (for it me confused before) but i wanted know if there was some trick or other solution for to have a situation as this without error.
Thanks again.
You could turn the function into a procedure taking a var parameter:
myclass = class
public
procedure Funct1(var AResult: String); overload;
procedure Funct1(var AResult: Integer); overload;
end;
Firstly, for functions to be overloaded, they have to be named the same.
Secondly, this is not possible to do. Overloaded functions must have different parameters.
In your case, there is no way the compiler can tell which of your functions to call (assumed that both are renamed to Funct1):
var
v: Variant;
mc: myclass;
begin
v := mc.Funct1;
end;
What you post doesn't make sense. The first example should compile without any problem, as the functions have different names, Funct1 and Funct2.
Problems only arise when methods (or functions) have the same name. Then, normally, an overload directive would be in order, but overload can't distinguish functions on return value alone.
So assuming the names are the same, what you want is impossible. There is no way to overload these functions, if they don't have a different parameter signature. You can just give them different names, which is preferrable anyway, as they apparently do different things.
FWIW, your question is flawed by the fact that you apparently did not post the exact code with which you are actually having problems. Please always post the exact code that causes your problems, and if there are error messages, always post the exact error message (they can usually be copied using the usual copy keystrokes, e.g. Ctrl+C, even in most parts of the IDE or in message dialogs in Delphi). If there are any line numbers in the error message, indicate this in the source code you post, as we don't always have the same line numbers as you have.
if you want to overload methods with different return types, just add a dummy parameter with default value to allow the overload execution, but don't forget the parameter type should be different so the overload logic works e.g:
type
myclass = class
public
function Funct1(dummy: string = EmptyStr): String; overload;
function Funct1(dummy: Integer = -1): Integer; overload;
end;
use it like this
procedure tester;
var yourobject : myclass;
iValue: integer;
sValue: string;
begin
yourobject:= myclass.create;
iValue:= yourobject.Funct1(); //this will call the func with integer result
sValue:= yourobject.Funct1(); //this will call the func with string result
end;
also don't forget that this way your gonna have a problem with variants like in this example:
procedure tester;
var yourobject : myclass;
vValue: variant;
begin
yourobject:= myclass.create;
vValue:= yourobject.Funct1();
end;
until you implement the variant function too:
function Funct1(dummy: Variant = Unassigned): Variant; overload;
As was already stated, you cannot do it as you are wanting to do. However, you could just as easily implement each function with a different name such as "AsInteger" or "AsString". Your code will be clearer and this is generally the way it is done within the VCL.
TmyClass = class (TObject)
public
function AsString : string;
function AsInteger : Integer;
end;

Untyped / typeless parameters in Delphi

What type are parameters without type like in the class TStringStream:
function Read(var Buffer; Count: Longint): Longint; override;
What is the type of Buffer parameter (is it a type of Pointer ?).
I wrote an article about this very topic a few years ago:
What is an untyped parameter?
Untyped parameters are used in a few situations; the TStream.Read method you ask about most closely matches with the Move procedure I wrote about. Here's an excerpt:
procedure Move(const Source; var Dest; Count: Integer);
The Move procedure copies data from an arbitrary variable
into any other variable. It needs to accept sources and destinations of
all types, which means it cannot require any single type. The procedure
does not modify the value of the variable passed for Source, so that
parameter’s declaration uses const instead of var, which is the
more common modifier for untyped parameters.
In the case of TStream.Read, the source is the stream's contents, so you don't pass that in as a parameter, but the destination is the Buffer parameter shown in the question. You can pass any variable type you want for that parameter, but that means you need to be careful. It's your job, not the compiler's, to ensure that the contents of the stream really are a valid value for the type of parameter you provide.
Read the rest of my article for more situations where Delphi uses untyped parameters.
Check out the Delphi help for "Untyped parameters"
You can pass in any type, but you have to cast it in the implementation. Help says that you cannot pass it a numeral or untyped numeric constant. So basically you have to be know what type to expect, and compiler can not help you, so you need a good reason to do it this way. I suppose it could be of use if you need the method to handle incompatible types, but then again you could write several overloaded versions for each expected type, I would suggest that as a better solution.
Perhaps surprisingly, it is legal to pass a dereferenced pointer as an untyped parameter. And the pointer itself doesn't even have to have a type.
procedure SomeMethod(var aParameter);
∶
procedure CallSomeMethod(aIsInteger : Boolean);
type
buffer : Pointer;
intValue : Integer;
realValue : Single;
begin
if aIsInteger then
begin
buffer := #intValue;
end
else
begin
buffer := #realValue;
end;
SomeMethod(buffer^);
Of course it would probably have been easier if the parameter to SomeMethod() had been a pointer, but this might not be under your control.
var in a parameter list is the Delphi syntax for call by reference. It can be typed as e.g. the AllowChange parameter in the OnChanging handler of an Listview:
procedure TSomeForm.LVOnChanging(Sender: TObject; ...; var AllowChange: Boolean);
begin
if SomeProblemOccurred then
AllowChange := False;
end;
or untyped as in your example.

Function & Procedures in Delphi (Defaults & Optional Parameters)

EDIT:
Is there a better way of doing this ?
TPendingBets = class(TDataModule)
private
public
function GetBdy(out IdEvent : Integer ) : Boolean; overload;
function GetBdy(out IdEvent : Integer; out idBetType : TBetTypes) : Boolean; overload;
function GetBdy(out IdEvent : Integer; out idBetType : TBetTypes; Out TotalOrgStake,Price : Double; out PriceError :Boolean): Boolean; overload;
end;
implementation
////////////////////
function TPendingBets.GetBdy(out IdEvent : Integer ): Boolean;
var idBetType : TBetTypes;
TotalOrgStake,Price : Double;
PriceError :Boolean;
begin
result := GetBdy(IdEvent,idBetType,TotalOrgStake,Price,PriceError);
end;
////////////////////
function TPendingBets.GetBdy(out IdEvent : Integer; out idBetType : TBetTypes): Boolean;
var TotalOrgStake,Price : Double;
PriceError :Boolean;
begin
result := GetBdy(IdEvent,idBetType,TotalOrgStake,Price,PriceError);
end;
////////////////////
function TPendingBets.GetBdy(out IdEvent : Integer; out idBetType : TBetTypes; Out TotalOrgStake,Price : Double; out PriceError :Boolean): Boolean;
begin
result := false;
if cdBdy.Eof = False then
begin
IdEvent := cdBdy.FieldByName(IdEvent ).AsInteger);
idBetType := TBetTypes(cdBdy.FieldByName(idBetType ).AsInteger);
//.
//.
result := True;
end;
end;
NOTE:
As my initial question was not very clear I have removed part of it
Overloading does not mean having 5 copies of the code in the function. You might have 5 functions, but 4 of them just call the main function with the correct parameters.
I generally don't like out parameters. Have you considered returning a record containing all the results?
Fn1Result = record
A: Extended;
B: Integer;
C, D, E, F: Extended
S: String;
end;
Then declare your function like:
function Fn1: Fn1Result;
begin
Fn1.A := C_ConstA;
// etc. . . .
end;
Naturally I am assuming you are not using output parameters as input. Per the Delphi help:
An out parameter, like a variable parameter, is passed by reference. With an out parameter, however, the initial value of the referenced variable is discarded by the routine it is passed to. The out parameter is for output only; that is, it tells the function or procedure where to store output, but doesn't provide any input.
I get a funny feeling you are using output parameters as input parameters. Don't. Pass them as var if you want them to go both ways.
I think the overload solution is quite a good solution, at least it is one. Other programming languages require you to declare functions with different names.
Another approach may be a dynamic array parameter like:
type
TExtendedDynArray = array of Extended; // if not already declared elsewhere
function Fn1(out arr: TExtendedDynArray): Boolean;
In addition to what Uwe wrote (and I agree that overloading is a better solution here; up voted), you should never presume an out parameter to have any value at all at the start of the function. That's what out means.
You can use overloaded function something like this
function Fn1(Out A:string; B:integer=5) : boolean; overload;
function Fn1(Out A:string) : boolean; overload;
you must to define both functions.
As Jim showed, you don't need to copy the code around when overloading.
I don't like much to mix parameters with default values and overloading, because (doing it without care) you can create a mess.
Normally, if I have to support a lot of syntaxes:
1) I define a complete signature, with ALL PARAMETERS. Let's call it the
'master' one.
2) All overloads call that one. Or call one to another, but in the end every
overload chain must call the 'master' one to execute the task.
For me, overloads are "parameter crunchers" or "translators". Later, if I need
something to modify the master behavior, I create new parameters on 'master' (which can have default values - in fact, mostly have).
If I need a overload with the new parameter, I create a new one and it simply pass it to
the "master" procedure. The side effect is that the old ones continue to behave the same.
Default values can only be used for input parameters (that is: By value and const). For var and out parameters, default values are not possible.
As Jim already pointed out: You can avoid duplicating the code by having most overloaded functions call the one which takes all parameters, so they are merely thin layers over the original function. From Delphi 2005 on you can declare them inline, so the compiler might actually generate more efficient code.

Resources