Property using Generics in Delphi - delphi

I'm trying to write a property which uses generics:
type TMyClass = class
protected
function GetCountBy<T: Class>: Integer;
public
property CountBy<T: Class>: Integer read GetCountBy<T>;
end;
but the compile fails on the property declaration with the message 'Property CountBy does not exist in base class', and the red squiggle on the opening < of the property name.
Is there any way to achieve this?
Edit:
Here's my other use case, which is more complex but more real world:
property ItemsBy<T: Class>[Index: Integer]: T read GetItemsBy<T> write SetItemsBy<T>;
The function filters the contents of a list to return the Index'th item of the specified class.

Generic properties are not supported in Delphi. Only generic classes, or generic methods.
I can't find anything in the documentation that explicitly states that limitation. On the other hand the documentation only describes generic classes and generic methods. And the new language grammar to support generics also makes no mention of properties.

I'm not up to speed on generics but shouldn't the declaration be more like this
type TMyClass<T: class> = class
protected
function GetCountBy<T>: Integer;
public
property CountBy<T>: Integer read GetCountBy<T>;
end;

Related

How to extend a Generic Collection in Delphi?

I have a Generic collection like this:
TFoo = class;
TFooCollection<T: TFoo> = class(TObjectDictionary<string, T>)
procedure DoSomething;
end;
It works fine.
Now I need to extend TFooCollection like this:
TBar = class( TFoo );
TBarCollection<T: TBar> = class(TFooCollection)
procedure DoSomethingElse;
end;
And the compiler complains that TFooCollection isn't defined.
As TBar is inheriting from TFoo, I would like to take advantage of TFooCollection methods (that would work with TFoo and TBar items) and do something else just with TBar Collections.
Is it possible in Delphi?
You knew how to extend the generic collection TObjectDictionary, so simply apply that same technique when extending the generic collection TFooCollection. TObjectDictionary doesn't specify a type by itself — you needed to provide values for its two generic type parameters. One you hard-coded to string, and the other you provided by forwarding the generic type parameter received in TFooCollection.
Likewise, when specifying the base type for TBarCollection, you can either provide a hard-coded value for the TFooCollection type parameter, or you can forward the parameter from TBarCollection. You probably want to do the latter:
type
TBarCollection<T: TBar> = class(TFooCollection<T>)
end;
Remember that in generic classes, the type declarations are part of the type name.
The compiler is quite correct is stating that you do not have a TFooCollection type defined. You have defined a type called TFooCollection (or TFooCollection).

FreePascal RTTI. Is there a way to invoke method?

I'm trying to find out if there is a way to do things similar to Delphi's enhanced RTTI features.
As far as I know FPC doesn't provide RTTI features which appeared in Delphi since Delphi 2010. But I'd like to find some way to do a few tricks during runtime.
Using typinfo unit in FPC I can do such things as:
get Object published property list - via getPropList from typinfo unit;
get/set value of the Object's published property - via GetPropValue(...): Variant/SetPropValue(...Value: Variant);
get published method - via MethodAddres;
But I haven't found a way to do things like:
call methods;
call constructors, or create Objects;
Update: the problem with constructors is much like methods one - I want to have a way to pass different params in it:
// concept of code
type
TClass = class of TObject;
TMyClass1 = class
public
constructor Create(Param1: Integer; Param2: string); override;
end;
TMyClass2 = class
public
constructor Create(ObjectParam: Object); override;
end;
TParams = array of Variant;
var
Classes: array of TClass
Instances: array of Object;
ParamArray: array of TParams;
...
For I := 0 to Count-1 do
begin
LocalConstructor := #(Classes[I].Create);
Instances[I] := CallConstructor(LocalConstructor, ParamArray[I]);
end;
So I need to call constructor without knowing its signature.
So my problem is to call an Object's method and pass some parameters to it. It could look like function CallMethod(Instance: Object; MethodName: String; Params: array of Variant): Variant;
If I'm not mistaken it could be solved via Delphi's 2010+ RTTI. But before using enhanced Delphi's RTTI I'd like to understand is it possible in FPC.
In other words my current problem is pass arguments to a routine.
I know it can be done using this scheme:
type
TmyProc = procedure CallMe(x: byte);
...
var proc: TmyProc;
...
proc := pointerToFunc^;
proc(0);
But I need to implement it without knowing count and types of parameters (during compile time).
There are a few links related to the topic:
Delphi: Call a function whose name is stored in a string
http://www.swissdelphicenter.ch/torry/showcode.php?id=1745
The second article (http://www.swissdelphicenter.ch/torry/showcode.php?id=1745) describes a way to pass arguments to a routine imported from DLL by name. Which is almost that I need I suppose. But I'm not sure that way is reliable.
Maybe there's any library, which implements these things using "old" typinfo unit (without RTTI unit)?
Also I'm interested in a way to create some kind of universal event handlers - procedures which can be assigned to different events (with different sets of parameters) e.g.:
procedure myEventHandler(params: array of variant);
...
Button.OnClick := myEventHandler;
Button.OnMouseMove := myEventHandler;
is this possible? Or at least something similar to it?
You can call methods, even not published, using MethodAddress, but it's up to you to ensure correct argument list.
You can call constructors using metaclasses (class references), example of it could be seen in TCollection: you pass class of your collection item at runtime and then it can be created when needed. By defining abstract class with virtual (and probably abstract) constructor, you can come up with argument list you wish, some example here.
AFAIK there is no way to determine argument list at runtime, but if you design both the methods to call and caller itself, there are many ways you can implement similar behavior.
For example, you pass variant open array (Array of const), as it's done in Format(), so number of arguments and its type may vary. But even having one and only pointer as the argument, you sure can pass as many as you want, all you need to do is to come up with some class to which it will lead.

DCC error : published field is not a class or interface type

I keep getting this following DCC error, published field 'name' is not a class or interface type for the following class.
TGroup = class
name:string[32]; <<<========================
rwFeatures:TFeatures;
roFeatures:TFeatures;
levels:TLevels;
private
public
constructor Create;
procedure Read(var f:file);
procedure ReadOld(var f:file);
procedure Write(var f:file);
end;
What does it mean?
The class is compiled with the Emit runtime type information setting enabled. When the class is compiled with runtime type information, the default visibility is published. Which means that the short string field is published. And short string fields are not allowed to be published.
The documentation says:
Fields can be published only if they are of a class or interface type.
That's a pretty stringent requirement. It means that you can't publish integer or boolean fields, for example.
I suspect this limitation is because the primary use for published fields is for object references. Think of the components on a form.
Solve the problem using one of these options:
Don't emit runtime type information for this class.
Make the short string fields public rather than published.
Use properties rather than fields.

Store Generic Array in Class using Delphi XE

Using the structure below, how can I define my TAnimalCollection class in order to store my collection? Calling either SelectAll or SelectTop10 will update the SelectedRecords.
Removing the private field allows the code to compile, but there is no mechanism to then store the returned result set.
TDog = class
private
FBreed: string;
public
property Breed: string read FBreed write FBreed;
end;
TCat = class
private
IsWild: string;
public
property IsWild: string read FIsWild write FIsWild;
end;
TMyArray<T> = array of T;
TAnimalCollection = class
private
SelectedRecords: TMyArray<T>; // Generates: Undeclared Identifier: 'T'
public
function SelectAll<T>: TMyArray<T>;
function SelectTop10<T>: TMyArray<T>;
// Other Methods
end;
First, you don't need TMyArray; the built-in TArray type does the same thing.
The compiler is correct, though. In your field declaration, there's no such thing as T. The generic argument needs to be introduced on the left of a declaration before it can be used on the right. If Delphi supported generic fields, the declaration would look like this:
SelectedRecords<T>: TArray<T>;
But it doesn't, and you wouldn't want it to in this case anyway. You apparently want to store two completely unrelated classes together in the same array simultaneously. An array is always of a single type. The only single type that unifies TDog and TCat is TObject, so your array needs to be of that type:
SelectedRecords: TArray<TObject>;
// or, more conventionally,
SelectedRecords: array of TObject;
You're welcome to declare a "generic array," but only as a field of a generic class or a variable of a generic method. If you could declare a standalone generic array, try to think of when the actual type of the array elements would be determined. If not at the point you declare the array, then when? With classes and methods, you specify the type argument(s) when you declare a variable of the class, instantiate the class, or call the method. Those are uses that are separate from their declarations, and each use is distinct. When you declare a variable, you must use it the same way you declared it — a variable's type cannot change without recompiling the program.

Delphi, is possible have a property and function with the same name?

consider this code
type
TMyObject=class
private
FName: Integer;
function Name: Integer;
public
property Name : Integer read FName;
function Name(Param1 : integer) : Integer; overload;
end;
Is possible in delphi create a property and a function (or procedure) with the same name?
Exist any directive or compiler switch which allow create a class like that?
No, it is not. In addition, there is no directive or compiler switch that would allow this.
The answer is in the error message that you got when you tried this...you did try it right?
Since i see the overload keyword. I suspect that perhaps what you need default are parameters
If you use
function Name(Param1: Integer = SOME_VALUE): Integer;
it can be called as either :=Name or :=Name(5)
By accident I discovered it is possible to have a class with a property that has the same name as a function in the parent class (or vice versa).
However, I would avoid this because it will confuse you. Especially if the function and property have different meanings!
Currently, the compiler cannot do what you want.
In theory, a future version of the compiler could:
The signature of a method (overloaded or not) consists of the name and the parameter types.
The same holds for the signature of indexed properties.
Since the signature spaces of properties and methods are partially linked (hence the compiler error message), that combined space could be extended to include property overloads.
Of course that extension can backfire because of backward compatibility.
--jeroen

Resources