Differences in calls of generic class functions in Delphi and Lazarus / FPC - delphi

I have a project that should compile under Delphi and FPC, with generic class procedures. They compile to Delphi and FPC.
The calls to these procedures only compile to Delphi. To FPC, a specialization of the generic types must be given.
I want to omit this specializations in the calls, because there are maybe thousands of such calls to these procedures in the project. I would have to pick out the corresponding types for each call.
However, with specializations added in the calls, everything compiles in both directions.
// a simpified example of a generic class function
{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
interface
type
TMyClass = class
class function MyFunc<A, B> ( bool : boolean;
var Data : A;
var Key : B): cardinal;
end;
implementation
class function TMyClass.MyFunc<A, B>( bool : boolean;
var Data : A;
var Key : B): cardinal;
begin
// do something
end;
procedure callGen;
var
i : integer;
s : string;
begin
TMyClass.MyFunc(true,i, s);// compiles only to delphi
TMyClass.MyFunc<integer, string>(true,i, s);// compiles in both directions
end;
I want to omit the insertion of <integer, string> in the call, for in the project there are a huge number of of different types to insert and even more calls exist.
Error message in Lazarus for
TMyClass.MyFunc(true,i, s);
Fatal: Syntax error, ")" expected but "," found

Related

How do I resolve the error "E2010 Incompatible types: 'TGUID' and 'T'"?

This is a bit puzzling for me as I'm working on an unit with several dozens of interfaces that are all based on this base interface definition:
type
IDataObject = interface(IInterface)
['{B1B3A532-0E7D-4D4A-8BDC-FD652BFC96B9}']
function This: TDataObject;
end;
ISomeObject = interface(IDataObject)
['{7FFA91DE-EF15-4220-A43F-2C53CBF1077D}']
<Blah>
end;
This means they all have a method 'This' that returns the class behind the interface, which is sometimes needed to put in listviews and stuff, but for this question it doesn't really matter because I want a generic class with additional functions that can be applied to any derived interface. (And any derived interface has their own GUID.) This is the generic class:
type
Cast<T: IDataObject> = class(TDataObject)
class function Has(Data: IDataObject): Boolean;
class function Get(Data: IDataObject): T;
end;
Doesn't look too complex and the use of class methods is because Delphi doesn't support global generic functions, unless they're in a class. So in my code I want to use Cast<ISomeObject>.Has(SomeObject) to check if the objects supports the specific interface. The Get() function is just to return the object as the specific type, if possible. So, next the implementation:
class function Cast<T>.Get(Data: IDataObject): T;
begin
if (Data.QueryInterface(T, Result) <> S_OK) then
Result := nil;
end;
class function Cast<T>.Has(Data: IDataObject): Boolean;
begin
Result := (Data.QueryInterface(T, Result) = S_OK);
end;
And this is where it gets annoying! Elsewhere in my code I use if (Source.QueryInterface(ISomeObject, SomeObject) = 0) then ... and it works just fine. In these generic methods the ISomeObject is replaced by T and should just work. But it refuses to compile and gives this error:
[dcc64 Error] DataInterfaces.pas(684): E2010 Incompatible types:
'TGUID' and 'T'
And that's annoying. I need to fix this but can't find a proper solution without hacking deep into the interface code of the System unit. (Which is the only unit I'm allowed to use in this code as it needs to run on many different platforms!)
The error is correct as QueryInterface expects a TGUID as parameter but it seems to get that from ISomeObject. So why not from T?
I think I'm trying to do the impossible here...
To be a bit more specific: Source.QueryInterface(ISomeObject, SomeObject) works fine without the use of any other unit. So I would expect it to work with a generic type, if that type is limited to interfaces. But that's not the case and I want to know why it won't accept T while it does accept ISomeObject.
Can you explain why it fails with a generic type and not a regular interface type?
QueryInterface() takes a TGUID as input, but an interface type is not a TGUID. The compiler has special handling when assigning an interface type with a declared guid to a TGUID variable, but that doesn't seem to apply inside of a Generic parameter that uses an Interface constraint. So, to do what you are attempting, you will just have to read the interface's RTTI at runtime to extract its actual TGUID (see Is it possible to get the value of a GUID on an interface using RTTI?), eg:
uses
..., TypInfo;
class function Cast<T>.Get(Data: IDataObject): T;
var
IntfIID: TGUID;
begin
IntfIID := GetTypeData(TypeInfo(T))^.GUID;
if (Data.QueryInterface(IntfIID, Result) <> S_OK) then
Result := nil;
end;
class function Cast<T>.Has(Data: IDataObject): Boolean;
begin
Cast<T>.Get(Data) <> nil;
end;
That being said, why are you duplicating functionality that the RTL already provides natively for you?
Your entire Cast class is unnecessary, just use SysUtils.Supports() instead (the SysUtils unit is cross-platform), eg:
uses
..., SysUtils;
//if Cast<ISomeObject>.Has(SomeObject) then
if Supports(SomeObject, ISomeObject) then
begin
...
end;
...
var
Intf: ISomeObject;
//Intf := Cast<ISomeObject>.Get(SomeObject);
if Supports(SomeObject, ISomeObject, Intf) then
begin
...
end;
Also, your IDataObject.This property is completely unnecessary, as you can directly cast an IDataObject interface to its TDataObject implementation object (Delphi has supported such casting since D2010), eg:
var
Intf: IDataObject;
Obj: TDataObject;
Intf := ...;
Obj := TDataObject(Intf);

Equal operator in Generic holding Records in Delphi

I have a list of generics in which I want to put either some records or some classes
TMyList<T> = class
private
fCount: Cardinal;
fItems: array of T;
public
constructor Create(aSize: Integer);
procedure UpdateItem(const x: T);
end;
But I can't have the compiling
procedure TMyList<T>.UpdateItem(const x: T);
var
I: integer;
begin
for I := 0 to fCount - 1 do
if fItems[I] = x then begin // <- error E2015
//do update
break;
end;
end;
It works for classes with this declaration: TMyList<T : class> = class, but then it can't hold records anymore.
Of course, for the record I declare class operator Equal(Left, Right : TMyRecord) : Boolean; so that MyRecord1 = MyRecord2 would compile.
This can never be made to work using the = operator. The reason is that generic constraints are not rich enough to specify the availability of operators. You simply cannot use the = operator on generic operands.
You can do so if you constrain the operands to be a class, because a classes are references, and the compiler knows how to compare references for equality. Basically the compiler needs to know how to generate the code when it compiles the generic class. Unlike C++ or Smalltalk templates, with generics the compiler does not wait until instantiation to compile the code.
If you want to use a custom comparer then you will need to supply that explicity. Which is rather frustrating, I know. If you can make do with the default comparer you can use:
TEqualityComparer<T>.Default

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;

Writing Delphi/FreePascal DLL to be called from gcc application

I need to make parts of my Win32 Delphi app available to another company's Linux gcc program.
Throughput and deployment requirements make any sort of remote service unsuitable so I'm looking at using FreePascal to build a .SO (Linux equivalent of a DLL) that the gcc app can call.
It's a long time since I used C/C++ and never on Linux so I'm a little unsure as to how best to structure the DLL/SO interface for compatibility with a gcc caller.
Here's a representation of my data structures
TFoo = record
x, y : double;
a : smallint;
b : string;
end;
TBar = record
a : double;
b : longint;
c : string;
end;
TFooBar = record
foo : array of TFoo;
bar : array of TBar;
end;
procedure Process(const inFooBar : TFooBar);
To make this Process method externally available via a FreePascal .SO how do I need to modify these declarations? I'm thinking something along the lines of
TFoo = record
x, y : double;
a : smallint;
b : PChar;
end;
TBar = record
a : double;
b : longint;
c : PChar;
end;
TFooBar = record
foo : ^TFoo;
foo_count : longint;
bar : ^TBar;
bar_count : longint;
end;
procedure Process(const inFooBar : TFooBar);
Am I on the right track? I don't have to get this exactly right, the other company's programmers will very likely fix up my mistakes. I just don't want them to laugh too hard when they see what I've sent them.
Use PAnsiChar instead of PChar, and integer instead of longint/smallint (since you use aligned records, there is no benefit of using a smallint field).
Define some pointer types like PFoo and PBar, which will be better written than ^TFoo e.g.
If you need to access some arrays, you could define TFooArray and TBarArray, as in the code below.
Don't forget the cdecl or stdcall calling convention for any function/procedure.
type
TFoo = record
x, y : double;
a : integer;
b : PAnsiChar;
end;
TBar = record
a : double;
b : integer;
c : PAnsiChar;
end;
TFooArray = array[0..maxInt div sizeof(TFoo)-1] of TFoo;
TBarArray = array[0..maxInt div sizeof(TBar)-1] of TBar;
PBar = ^TBar;
PFoo = ^TFoo;
PFooArray = ^TFooArray;
PBarArray = ^TBarArray;
TFooBar = record
foo : PFooArray;
foo_count : integer;
bar : PBarArray;
bar_count : integer;
end;
procedure Process(const inFooBar : TFooBar); cdecl; external 'ProcessGCCLibrary.so';
Will be your process read-only?
If the C part need to add some items, you'll have to provide some memory reallocation methods to the external library, mapping at least reallocmem().
Note that Delphi dynamic arrays can be mapped easily into C compatible structure, as such:
type
TFooDynArray: array of TFoo;
TBarDynArray: array of TBar;
procedure CallProcess(const aFoo: TFooDynArray; const aBar: TBarDynArray);
var tmp: TFooBar;
begin
tmp.foo := pointer(aFoo);
tmp.foo_count := length(aFoo);
tmp.bar := pointer(aBar);
tmp.bar_count := length(aBar);
Process(tmp);
end;
This could make your Delphi code much more readable. Take a look at our TDynArray wrapper if you want high-level access to such a dynamic array of records, with TList-like methods.
Since the AnsiString type maps a PAnsiChar, from the binary point of view, you can even define your records as such:
type
TFoo = record
x, y : double;
a : integer;
b : AnsiString;
end;
TBar = record
a : double;
b : integer;
c : AnsiString;
end;
When mapped to the gcc application, it will be read as a regular *char.
Using AnsiString here you won't need to handle memory allocation from your Delphi code. With an associated dynamic array, it could make your Delphi code much easier to maintain. Note that our TDynArray wrapper will handle nested AnsiString in records as expected, even for the highest level methods (e.g. binary serialization or hashing).
To ensure that the record packing/alignment/padding is as GCC expects, add {$packrecords c} to your Pascal source. Note that this directive is specific to the Free Pascal Compiler, Delphi does not support it.
Looks pretty good. Thoughts:
Your arrays (declared as a pointer and count) are going to be fully manually managed - that's fine for you?
You should change:
procedure Process(const inFooBar : TFooBar);
to include a C-compatible calling convention. I'm not sure what FreePascal and GCC both support, but something like cdecl should work fine.
You should also (for safety) specify the structure alignment / packing.
PChar should be explicitly narrow or wide (wide is normal these days.)

RTTI: Can I Get a Type by Name?

Given a text string containing a type name, is there some way to get the appropriate type itself?
I'm looking to do something like this:
type
TSomeType<T> = class
// yadda yadda
end;
procedure DoSomething;
var
obj : TObject;
begin
o := TSomeType<GetTypeByName('integer')>.Create;
// do stuff with obj
end;
I've looked at several RTTI explanations online and looked through the Delphi units and don't see what I'm looking for. Is this possible?
No, generics are entirely compiletime.
The new RTTI unit in Delphi 2010 has a way of retrieving types declared in the interface section of units. For any given type, represented by a TRttiType instance, the TRttiType.QualifiedName property returns a name that can be used with TRttiContext.FindType later to retrieve the type. The qualified name is the full unit name (including namespaces, if they exist), followed by a '.', followed by the full type name (including outer types if it nested).
So, you could retrieve a representation of the Integer type (in the form of a TRttiType) with context.FindType('System.Integer').
But this mechanism can't be used to retrieve instantiations of generic types that weren't instantiated at compile time; instantiation at runtime requires runtime code generation.
You can always register your types into some sort of registry (managed by a string list or dictionary) and create a factory function to then return the appropriate object. Unfortunately you would have to know in advance what types you were going to need. Something similar to the Delphi functions RegisterClass and FindClass (in the classes unit). My thinking is to put the generic template type into the list directly.
An example of possible usage:
RegisterCustomType('Integer',TSomeType<Integer>);
RegisterCustomType('String',TSomeType<String>);
if FindCustomType('Integer') <> nil then
O := FindCustomType('Integer').Create;
EDIT: Here is a specific simple implementation using a tDictionary from Generics.Collections to handle the registry storage...I'll leave extracting this into useful methods as a simple exercise for the reader.
var
o : TObject;
begin
TypeDict := TDictionary<String,TClass>.Create;
TypeDict.Add('integer',TList<integer>);
if TypeDict.ContainsKey('integer') then
o := TypeDict.Items['integer'].Create;
if Assigned(o) then
ShowMessage(o.ClassName);
end;
Another EDIT: I was giving this some thought last night, and discovered another technique that you can merge into this concept. Interfaces. Here is a quick do nothing example, but can easily be extended:
TYPE
ITest = interface
['{0DD03794-6713-47A0-BBE5-58F4719F494E}']
end;
TIntfList<t> = class(TList<T>,ITest)
public
function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
procedure TForm1.Button7Click(Sender: TObject);
var
o : TObject;
fTestIntf : ITest;
begin
TypeDict := TDictionary<String,TClass>.Create;
TypeDict.Add('integer',TIntfList<integer>);
if TypeDict.ContainsKey('integer') then
o := TypeDict.Items['integer'].Create;
if Assigned(o) and Supports(o,ITest,fTestIntf) then
ShowMessage(o.ClassName);
end;
of course you would have to implement the QueryInterface, _AddRef and _Release methods and extend the interface to do something more useful.
If you forget generics and basic types, the "RegisterClass" function would be helpful. But it doesn't work for generics or basic types.

Resources