"reference to function" as result of a function - delphi

i have a function that returns a function TFunc<Integer> which is reference to function:Integer.
and i have a procedure which takes a function TFunc<Integer> as argument, calls it and prints its result.
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
function GetFunction:TFunc<Integer>;
begin
result := function:Integer begin result := 42 end;
end;
procedure FunctionCall(AFunc:TFunc<Integer>);
var i:Integer;
begin
i := AFunc;
WriteLn(Format('Function Result = %d',[i]));
end;
begin
// FunctionCall(GetFunction); // error
FunctionCall(GetFunction()); // works as excpected
end.
this call (FunctionCall(GetFunction);) results in an error. and the call with () works as excpected.
my question is:
when in delphi do i need brakets to call a function and when not (i thought that i never need them)
or
shouldn't i need them and is it a bug?
i work with delphi xe5 on windows 7 dcc32.

If what you report is correct (and see below for more on that), then you would have found a bug, I believe.
This code:
FunctionCall(GetFunction);
should not compile. Indeed it does not compile when I try to compile it in XE3, XE4, XE5, XE6 and XE7. It does not compile because, in this particular context, the compiler interprets GetFunction as being of type
function: TFunc<Integer>
All above mentioned compilers object with this error message:
[dcc32 Error] E2010 Incompatible types: 'System.SysUtils.TFunc' and 'Procedure'
So, if you have somehow (perhaps with some compiler options), managed to make that code compile then I can only believe that is due to a bug.
You should deal with this by applying the parentheses so that the compiler can understand that you wish to call GetFunction, not refer to it.
FunctionCall(GetFunction());

Related

Delphi 10.2 Tokyo - Casting object provided by interface

I'm trying to convert my aplliction from Delphi XE8 to 10.2 Tokyo. I'm getting strange runtime exeptions with casting objects provided by interfafce acrocss packages ( bpl's). when I try to cast objects with "as" keyword I'm getting
this exception during runtime:
Project Project1.exe raised exception class EInvalidCast with message
'Invalid class typecast'
Here is the code :
Interface in a separte package Plugin_interface.bpl :
unit MainIntf;
interface
Type IMainInft = interface
['{FE08C4A2-069C-4B8C-BB1B-445348CAB6A0}']
function GetForm : TObject;
end;
implementation
end.
Interface implamentation provided in Project1.exe :
unit MainImpl;
interface
uses MainIntf;
Type TMain = class(TInterfacedObject,IInterface,IMainInft)
function GetForm : TObject;
end;
implementation
uses unit1;
function TMain.GetForm: TObject ;
begin
result:=Form1; // interafce is implemented on the main form so Form1 is rechable form here
end;
end.
And finally in another package "plugin.bpl" I'm trying to obtain object from interface :
unit Plugin_main;
interface
uses Mainintf, Vcl.Forms;
type TPlugin = class (Tobject)
IIMyRefernceToMianIntf: IMainInft;
end;
function RegisterPlugin(AMainIntf: IMainInft): TForm ; export;
procedure UnRegisterPlugin; export;
exports
RegisterPlugin,
UnRegisterPlugin;
var
Plugin_obj: TPlugin;
implementation
uses vcl.Dialogs,System.Classes ;
function RegisterPlugin(AMainIntf: IMainInft): TForm ;
var
MyForm : TForm ;
begin
Plugin_obj:=TPlugin.Create;
Plugin_obj.IIMyRefernceToMianIntf:=AMainIntf;
if AMainIntf.GetForm is TForm then
Showmessage ('Great it is a Tform') // will not happen
else
Showmessage ('Sorry it is not Tform'); // will happen
if TComponent (AMainIntf.GetForm).Classname='TForm1' then
Showmessage ('What ?? It is TForm1 decsendant from TForm so is it TForm after all ?!'); // will happen
// result:= AMainIntf.GetForm as TForm -- This will rise na exception
result:= TForm( AMainIntf.GetForm) ; // this will work
end;
procedure UnRegisterPlugin;
begin
Plugin_obj.Free;
end;
end.
Why cant I use "as" and "is" keyword .
Only hard catsing will do, but i hate to do it .
on XE8 compiler everything worked as expected - problem exists on XE 10.2 tokyo compiler
The "is" keyword checks the actual objects to see it is of the type you are asking. So, checking for this:
if AMainIntf.GetForm is TForm then
Showmessage ('Great it is a Tform') // will not happen
does not happen because GetForm returns TObject and not TForm. Checking with "is" means, also, that you are checking for castability, i.e. the ability to use the "as" keyword. Since, the "is" check fails, that command fails as well:
result:= AMainIntf.GetForm as TForm;
Your next option here is to hard-cast GetForm the way you do it:
TForm(AMainIntf.GetForm);
which works because this casting does not check whether GetForm is of TForm type. Since you return a form in TMain, this hard-casting is safe bet for you.
Having said that, however, why don't you return TForm directly rather than TObject? Do you use IMainInft in other classes that return other types than TForm?

Casting literals to PChar / PAnsiChar

I've got really stupid question...
Why this code:
PChar('x');
causes "Access violation" error?
Compiler optimalisation?
Example:
var s: String;
...
s := StrPas(PAnsiChar('x'));
This causes AV in Delphi 5 / Delphi XE
Or this one:
Windows.MessageBox(0, PChar('x'), PChar('y'), 0);
This causes AV in Delphi 5, but not in Delphi XE
In XE there is an empty MessageBox
Console example:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, Windows;
var s: String;
begin
s := StrPas(PChar('xxx')); // EAccessViolation here
end.
StrPas(PAnsiChar('x'));
I posit that 'x' is treated as a character literal rather than a string literal. And so the cast is not valid. If so then this will work as you would expect
StrPas('x');
due to an implicit conversion. Or
StrPas(PAnsiChar(AnsiString('x')));
thanks to the explicit conversion.
I think the former is probably to be preferred. Literals don't need casting to null terminated pointer types. The compiler can emit the correct code without the cast. And casts always run the risk of suppressing an error.

Why does Format reject procedure address arguments starting with XE4

Consider this program:
{$APPTYPE CONSOLE}
uses
System.SysUtils;
procedure Foo;
begin
end;
type
TProcedure = procedure;
const
FooConst: TProcedure = Foo;
var
FooVar: TProcedure = Foo;
P: Pointer;
{$TYPEDADDRESS ON}
begin
P := #Foo;
Writeln(Format('%p', [P]));
Writeln(Format('%p', [#FooConst]));
Writeln(Format('%p', [#FooVar]));
Writeln(Format('%p', [#Foo]));
Readln;
end.
This program compiles and runs on XE3 and produces the following output:
00419FB8
00419FB8
00419FB8
00419FB8
On XE4 and later the program fails to compile, with error messages on both of these lines:
Writeln(Format('%p', [#FooConst]));
Writeln(Format('%p', [#FooVar]));
[dcc32 Error] E2250 There is no overloaded version of 'Format' that can be called
with these arguments
On XE4, XE5 and XE6, the program compiles when $TYPEDADDRESS is switched off. On XE7, the program fails to compile irrespective of the setting of $TYPEDADDRESS.
Is this a compiler bug? Or am I using incorrect syntax to obtain the address of a procedure?
I believe that this is a compiler bug and have submitted a QC report: QC#127814.
As a work around you can use either of the following:
Use addr() rather than the # operator.
Cast #FooVar or #FooConst to Pointer, e.g. Pointer(#FooVar).
I think that the behaviour of the new compiler XE7 is more consistent with the specification and the error need to be shown in this case, since the {$TYPEDADDRESS ON} enforce the # operator to return a typed pointer and the Format function instead gets as input an untyped generic pointer.
Since the the purpose of the {$TYPEDADDRESS ON} is encouraging careful use of pointers, catching at compile time unsafe pointer assignments, it is right that if the function expects a generic untyped pointer(and in this case make sense because the function purpose is to print the address of it - so no need to have typed pointer to retrieve its address), the compiler will catch an error in the case of a typed pointer is passed, the behaviour is consistent with the specification.
I think that in this case the right way to go(based on the documentation) would be :
Writeln(Format('%p', [Addr(FooConst)]));
Writeln(Format('%p', [Addr(FooVar)]));
since the Addr function always returns an untyped Pointer that is what the Format with %p expects and needs.
What I assume is that in previous versions the compiler, in a case like this one, used to perform an automatic cast : Pointer(#FooConst) , but it doesn't make too much sense because of the {$TYPEDADDRESS ON} directive .

Delphi mocks with DSharp won't compile with enumerator values

I'm using DSharp mocks in Delphi XE5 (thanks Stefan!!) but have a problem with some enumerators. If I try to define an enumerator with specific values, the {$M+} directive causes the following error:
E2134 Type 'TMyEnum' has no type info
Here is a sample console app which sums it all up:
program DSharpMockEnum;
program DSharpMockEnum;
{$APPTYPE CONSOLE}
{$R *.res}
uses
DSharp.Testing.Mock,
System.SysUtils;
type
TBadEnum = (badEnum1 = 1, badEnum2); // <---- WONT COMPILE
TGoodEnum = (goodEnum1, goodEnum2); // This compiles OK
{$M+}
IBadMock = interface
['{34B3904E-3EBA-4C6E-BBA8-A40A67A32E7F}']
function GetEnum: TBadEnum;
procedure SetEnum(Value: TBadEnum);
property MyEnum: TBadEnum read GetEnum write SetEnum;
end;
{$M+}
IGoodMock = interface
['{34B3904E-3EBA-4C6E-BBA8-A40A67A32E7F}']
function GetEnum: TGoodEnum;
procedure SetEnum(Value: TGoodEnum);
property MyEnum: TGoodEnum read GetEnum write SetEnum;
end;
var
LGoodMock: Mock<IGoodMock>;
LBadMock: Mock<IBadMock>;
begin
try
Writeln('Good Mock');
LGoodMock.Setup.WillReturn(goodEnum1).Any.WhenCalling.MyEnum;
Writeln('Bad Mock');
LBadMock.Setup.WillReturn(badEnum1).Any.WhenCalling.MyEnum;
Writeln('Stop');
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Any ideas or suggestions? I prefer to avoid resetting all my enums since some are stored in the database and need specific values.
Delphi XE5.
Thanks.
Rick.
answer in error log. Enums with assigned values does not have RTTI. If u want enums with rtti and concrete values, u must use dummy emeration entries, like
TBadEnum = (badDummy0, badEnum1, badEnum2,badDummy3, badEnum4);
Here badDummy0 effectively stores 0, and badDummy3 is 3
Other option is to use some integer type

Delphi - Interface inheritance with generics

I am currently stuck with a compiling error, no one in our company can help and I am sadly not finding the correct search patterns for SO or google.
As code I am using 2 Interfaces, inherited and 2 Classes, inherited.
The following code reproduces the error:
program Project22;
{$APPTYPE CONSOLE}
type
IStorageObject = interface(IInterface)
end;
TObjectStorage<T: IStorageObject> = class(TObject)
end;
IKeyStorageObject<TKey> = interface(IStorageObject)
end;
TKeyObjectStorage<TKey, T: IKeyStorageObject<TKey>> = class(TObjectStorage<T>)
end;
TImplementingClass<TKey> = class(TInterfacedObject, IKeyStorageObject<TKey>)
end;
begin
TKeyObjectStorage<Integer, TImplementingClass<Integer>>.Create;
end.
The compiler error for 'TKeyObjectStorage' is:
[DCC Error] Project22.dpr(11): E2514 Type parameter 'T' must support interface 'IStorageObject'
What I think is, that the compiler is not recognizing that Parameter T of the Class 'TKeyObjectStorage' correctly.
It should be correct, since the wanted Type 'IKeyStorageObject' has the parent type IStorageObject.
Why is this not working? What am I doing wrong? Is this not possible in Delphi?
Update
The original question had a problem which I identified (see below). However, the fix I describe there is fine for XE3 and later, but that program below does not compile in XE2. Thus I conclude that this is an XE2 generics compiler bug.
Anyway, here's a workaround for Delphi XE2:
{$APPTYPE CONSOLE}
type
IStorageObject = interface(IInterface)
end;
TObjectStorage<T: IStorageObject> = class(TObject)
end;
IKeyStorageObject<TKey> = interface(IStorageObject)
end;
TKeyObjectStorage<TKey; T: IKeyStorageObject<TKey>, IStorageObject> = class(TObjectStorage<T>)
end;
TImplementingClass<TKey> = class(TInterfacedObject, IStorageObject, IKeyStorageObject<TKey>)
end;
begin
TKeyObjectStorage<Integer, TImplementingClass<Integer>>.Create;
end.
Original answer
It would have been better if you had provided a complete program that exhibited the compiler error. You need to attempt to instantiate an object to see that error.
But, I think I've reproduced your problem. So I believe that the issue is that this code:
TKeyObjectStorage<TKey, T: IKeyStorageObject<TKey>> = ...
applies the generic constraint to both TKey and T. Now, clearly you only want the constraint to apply to T so you'll need to write:
TKeyObjectStorage<TKey; T: IKeyStorageObject<TKey>> = ...
Here's a short program that compiles following the change in Delphi XE3:
{$APPTYPE CONSOLE}
type
IStorageObject = interface(IInterface)
end;
TObjectStorage<T: IStorageObject> = class(TObject)
end;
IKeyStorageObject<TKey> = interface(IStorageObject)
end;
TKeyObjectStorage<TKey; T: IKeyStorageObject<TKey>> = class(TObjectStorage<T>)
end;
TImplementingClass<TKey> = class(TInterfacedObject, IKeyStorageObject<TKey>)
end;
begin
TKeyObjectStorage<Integer, TImplementingClass<Integer>>.Create;
end.
This is quite a nuance, the changing of a comma to a semi-colon. Programming by significant punctuation is never much fun. That said, you are familiar with the difference between commas and semi-colons in formal parameter lists and so it should not come as too much of a surprise to see the same distinction drawn here.
The documentation does cover this mind you:
Multiple Type Parameters
When you specify constraints, you separate multiple type parameters by
semicolons, as you do with a parameter list declaration:
type
TFoo<T: ISerializable; V: IComparable>
Like parameter declarations, multiple type parameters can be grouped
together in a comma list to bind to the same constraints:
type
TFoo<S, U: ISerializable> ...
In the example above, S and U are both bound to the ISerializable
constraint.

Resources