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
Related
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());
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 .
I get an AccessViolation when running the following simple code with Delphi XE5:
Access violation at 0x0040213c: write of address 0x00000000
This runs without any problems with Delphi 2009! What has changed since than?!
program consola;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Classes;
type
MyRecord = Record
name : string;
age : Integer;
end;
var
recPointer : ^MyRecord;
FList: PPointerList;
begin
New(recPointer);
recPointer.name := 'Brian';
recPointer.age := 23;
GetMem(FList, 4 * SizeOf(recPointer));
FList^[0] := recPointer;
end.
Have you seen declaration of PPointerList on XE5? This is pointer to dynamic array now
Extract from help:
Delphi type PPointerList = ^TPointerList;
Delphi type TPointerList = array of Pointer;
so possible usage is:
New(Flist);
SetLength(Flist^, 4);
FList^[0] := recPointer;
(of course, PPointerList is useless here, it's enough to exploit TPointerList)
You are using the wrong types here. Your FList variable is a pointer to a dynamic array of untyped pointers. That is not what you need at all. And you don't allocate the memory properly. I'm not going to try to explain more about your code because your current approach is fundamentally flawed.
You should use either a dynamic array or TList<MyRecord>. The former looks like this:
var
Arr: TArray<MyRecord>;
....
SetLength(Arr, 4);
Arr[0].Name := ...
And the latter:
var
List: TList<MyRecord>;
Rec: MyRecord;
....
List := TList<MyRecord>.Create;
Rec.Name := ...
List.Add(Rec);
Your code is a recipe for disaster. Even if you fix the types to allow you to allocate using GetMem, how are you going to handle management of the managed string member of your record. Make life easy by using type safe data structures and so allow the compiler to do the heavy lifting.
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.
I'm trying to use generic records with RTTI, but ran into a problem with Type Info. Does anyone know why the following won't compile using Delphi 2010?
program GenericTypeInfo;
{$APPTYPE CONSOLE}
uses
TypInfo,
SysUtils;
type
TMyRec<T> = record
public
Value: T;
end;
TMyInt = TMyRec<Integer>;
TMyString = TMyRec<String>;
begin
try
Writeln(GetTypeName(TypeInfo(TMyRec<Integer>))); <--- This works fine
Writeln(GetTypeName(TypeInfo(TMyRec<String>))); <--- so does this
Writeln(GetTypeName(TypeInfo(TMyInt))); <--- BUT this won't compile
Writeln(GetTypeName(TypeInfo(TMyString))); <--- nor this!!
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
The lines indicated above generate the following compiler errors:
[DCC Error] GenericTypeInfo.dpr(24): E2134 Type 'TMyInt' has no type info
[DCC Error] GenericTypeInfo.dpr(24): E2134 Type 'TMyString' has no type info
I can't what's the big difference between the 2? I admit I'm not a low-level expert, but why does the compiler treat this differently? I need it to work for the TMyInt and TMyString types.
Thanks for any help.
This is a bug in Delphi 2010 which has been fixed for XE and higher.
But there is a workaround.
This example works fine in XE2. (And XE as #StefanGlienke comments).
RTTI and generics in Delphi-2010 lacks implementation in many ways.
Since the interpretation of types is done by compiler magic, the workaround is to upgrade.