I have message incompatible types - delphi

TServiceData = class
strict private
FPriority: Integer;
...
public
property Priority: Integer read FPriority write FPriority;
...
end;
TMonthData = class
strict private
function GetServiceData(AIdx: Integer): TServiceData;
public
...
property ServiceData: TServiceData read GetlstServiceData; // incompatible types - Why?
end;
Sorry. Typing error. Original
property ServiceData: TServiceData read GetServiceData;

Because you do not have argument in the property.
property ServiceData: TServiceData read GetlstServiceData;
See - the GetlstServiceData here can have no arguments because Delphi has nowhere to take them from its only available information source at compile type - the very property ServiceData: TServiceData declaration.
You should either add the argument to the property or to remove it from the function.
TMonthData = class
strict private
function GetValue0Args(): TServiceData;
function GetValue1Arg(const AIdx: Integer): TServiceData;
function GetValue2Args(const AIdx: Integer; const Flavour: string): TServiceData;
public
...
property Data0Args: TServiceData read GetValue0Args;
property Data1Arg[ SlotNumber: integer ]: TServiceData read GetValue1Arg;
property Data2Args[ SNum: integer; Recipient: string ]: TServiceData read GetValue2Args;
end;
See so called "array properties": http://docwiki.embarcadero.com/RADStudio/Seattle/en/Properties#Array_Properties
Though "array properties" are not actually arrays. The term was poorly selected: arrays would never take complex entities like strings or objects as their index while properties and functions do take them as their possible arguments.

Related

Can I pass the type of a generic variable as a parameter?

I can declare the type of a class by declaring:
type
TMyObject = class(TSomething);
TMyObjectClass = class of TMyObject;
I'm trying to do something like this:
IData<TIn,TOut> = interface;
IData = interface
function GetGenericData<Tin,TOut>: IData<TInput,TOut>;
function GetInType: TRttiType;
function GetOutType: TRttiType;
end;
Declare a base type that can give access to the generic type and give me info on what the generic Input and Output types are.
IData<TIn,TOut> = interface(IData)
['{5B402458-22EC-4A8B-83F3-C11AC575B79E}']
function GetInput(Index: Integer): TIn;
function GetInputCount: Integer;
function GetOutput(Index: Integer): TOut;
function GetOutputCount: Integer;
property Input[index: integer]: TIn read GetInput;
property Output[index: integer]: TOut read GetOutput;
property InputCount: integer read GetInputCount;
property OutputCount: integer read GetOutputCount;
end;
Here is the actual generic type that holds an 2 arrays of data. Note that the input and output does not need to be the same type.
I then have a class that contains one or more different types of IData.
TStage = class(TPipelineStage, IData);
TStage<TIn, TOut> = class(TStage, IData<TIn, TOut>);
TMyPipeline = class
fStages = TArray<TStage>; <<-- this is really a list of TStage<?,?>
I want to have a list of different TStages in my class, but obviously I cannot code that.
How do I access the TStage<IInterfaceA, InterfaceB> and TStage<InterfaceB, InterfaceB> from fStages?
Can I use class of .... in this context and instantiate an object from there?
The reason I'm using this approach is that I'm using a delegate in the Pipeline stage that is declared thus:
TDelegate<TIn, TOut> = reference to procedure(const Data: IData<TIn, TOut>);
There are a limitation in Delphi language that will prevent you to do things exactly the way you are trying. Take the code:
IData<TIn,TOut> = interface;
IData = interface
function GetGenericData<Tin,TOut>: IData<TInput,TOut>;
function GetInType: TRttiType;
function GetOutType: TRttiType;
end;
Here you have a generic method in a interface. Delphi (at least until XE3) does not support generic methods in interfaces.
In other snipet you have:
TStage = class(TPipelineStage, IData);
TStage<TIn, TOut> = class(TStage, IData<TIn, TOut>);
TMyPipeline = class
fStages = TArray<TStage>; <<-- this is really a list of TStage<?,?>
The answer for the question in code is No. That´s an array, not a list. However, since your array is holding instances of TStage, it will also hold instances of TStage<TIn, TOut> with no problem, no matter what types are TIn and TOut.
You have this question:
I want to have a list of different TStages in my class, but obviously I cannot code that.
How do I access the TStage and TStage from fStages?
Actually, you can! It´s possible to declare a list of a certain type (TStage in your case) and hold actual instances of descending classes, even a generic subclass.
It seems to me that you want a generic method to return the TStage instance already typed to a descending type, in order to avoid a type cast and some type tests, eventually returning nil if the stage is an instance of another class, right?
In this case, a generic method can be helpful, but only in classes, not in interfaces. I would do something like this (I didn´t have Delphi at hand to test the code, but the idea seems to fit your needs):
interface
type
TMyPipeline = class
private
FStages: TObjectList<TStage>;
public
// Constructors, destructor and other methods...
function StageAs<TIn, TOut>(aIndex: Integer): TStage<TIn, TOut>;
end;
implementation
function TMyPipeline.StageAs<TIn, TOut>(aIndex: Integer): TStage<TIn, TOut>;
var
stage: TStage;
begin
stage := FStages[aIndex];
if stage.InheritsFrom(TStage<TIn, TOut>) then
Result := TStage<TIn, TOut>(stage)
else
Result := nil;
end;

graph class for generic and non generic vertex types

a vertex class with an optional data field has been discussed SKIP GENERIC PARAMETER ON DEMAND; The best solution for me looks like this :
type
TVertex = class
public
Name: String;
OutputAttributes: TVertexOutputAttributes;
Marker: Boolean;
end;
type
TVertex<T> = class(TVertex)
public
Data: T; // User-defined data attribute
end;
While writing now the connected graph class I figured out a further problem :
TGraph = Class
private
Vertices: TObjectList<TVertex>;
....
function addVertex(u: TVertex): Integer;
function removeVertex(u: TVertex): TVertex;
end;
all function request the non generic version of my vertex class Tvertex right now. What is the best way to extend my Graph class to work with both vertex class definitions ( generic Tvertex and non generic one TVertex ) ? I tried the following code without success.
// a generic class working with 2 Tvertex class definitions ...
// this code does npot work :-(
TGraph<MyVertexType> = Class
private
Vertices: TObjectList<MyVertexType>;
....
function addVertex(u: MyVertexType): Integer;
function removeVertex(u: MyVertexType): MyVertexType;
end;
Your current code will not compile because you use TObjectList<T> which requires that T be a class. There is no constraint that enforces that. So you could add that constraint:
type
TGraph<MyVertexType: class> = class
FVertices: TObjectList<MyVertexType>;
...
end;
I do wonder whether you've fully thought through the lifetime ownership of the vertices. Using TObjectList<T> would imply that you intend the list to own the objects and destroy them when they are removed from the list. In which case
function removeVertex(u: MyVertexType): MyVertexType;
does not make sense.
Note that the definition above does not allow the graph class any knowledge of the capabilities of MyVertexType beyond the fact that it is a class. So perhaps you should constrain MyVertexType to be a vertex:
type
TGraph<MyVertexType: TVertex> = class
...
end;
This will allow the graph container to call vertex methods on its members.
type
TVertex = class
public
Name: String;
OutputAttributes: TVertexOutputAttributes;
Marker: Boolean;
end;
TIntVertex = class(TVertex)
public
Data: Integer;
end;
TSomethingElseVertex = class(TVertex)
public
Data: TSomethingElse;
end;
TGraph = class(TObjectList<TVertex>)
// any additional methods
end;
...
var
Graph: TGraph;
Vertex: TVertex;
...
Vertex := TIntVertex.Create;
Graph.Add(Vertex);
Vertex := Graph.Last;
if (Vertex is TIntVertex) then
(Vertex as TIntVertex).Data := 42;

Using nested type declared in a generic class within the generic interface that the class is implementing

I have a generic List-like class tMyList<B> with a method Each() which iterated over each element and calls anonymous procedure tMyList<A>.enumProc with paramenter - the current item of type <B>.
I want to implement the class as an Interface for easier lifetime management.
The problem is I cannot declare the Each method in the iMyList<A> interface, because tMyList<A>.enumProc type is unknown. As far as I know Interfaces does not support nested types?
Here is the code:
tMyList<B> = class;
iMyList<A> = interface
procedure each(enumProcedure: iMyList<A>.enumProc); // ERROR - Undeclared identifier: 'enumProc'
end;
tMyList<B> = class(tInterfacedObject, iMyList<B>)
type
enumProc = reference to procedure(item: iMyList<B>);
public
procedure each(enumProcedure: enumProc);
end;
* Implementing Enumerator is not an option in this particular case
The only way that you can make this work is to define the procedural type outside of the implementing class. Like this:
type
IMyIntf<A> = interface;
TMyProc<A> = reference to procedure(Intf: IMyIntf<A>);
IMyIntf<A> = interface
procedure Foo(Proc: TMyProc<A>);
end;
TMyClass<A> = class(TInterfacedObject, IMyIntf<A>)
procedure Foo(Proc: TMyProc<A>);
end;

Create new Pointer to Class

I want to create a Pointer to a Class named "CustomParam", so I declared
pCustomParam = ^CustomParam
The Class CustomParam got following class variables, which should be set to 0 in constructor:
var keyArray: array of String;
var valueArray: array of String;
var paramArray: array of pCustomParam;
var isParamArray: array of Boolean;
var size: Integer;
The Constructor looks like that:
constructor CustomParam.create;
begin
inherited;
size:= 0;
SetLength(keyArray,0);
SetLength(valueArray,0);
SetLength(isParamArray,0);
SetLength(paramArray,0);
end;
and was declared like that:
constructor create; overload;
Now I try to create the Pointer to CustomParam with "new" like following:
var pointerToCustomParam: pCustomParam;
begin
new(pointerToCustomParam);
But it don't jump to constructor of CustomParam Class. If I call the constructor manually like following:
pointerToCustomParam^.create;
The application will crash on SetLength commands.
What I noticed is, that the variable "pointerToCustomParam" got rubbish content direct after the "new" function.
I hope you are able to help me and the information are enough :)
Thank you :)
The proper way to create an instance of a type is to call the constructor on the type and assign the result to a variable of that type:
var
Param: CustomParam;
Param := CustomParam.Create;
Instances created that way are already references, so there's rarely a need for an additional pointer.
If you really must have a pointer, then start by declaring the type:
type
PCustomParam = ^CustomParam;
Then declare a variable:
var
Param: PCustomParam;
Allocate memory for the contents of the thing it points to:
New(Param);
That doesn't necessarily assign a valid value to the CustomParam reference it points to, but if it does, is assigns the value nil. So finally, assign a value to that newly allocated memory:
Param^ := CustomParam.Create;
Notice how we still have to call the constructor, and we never call the constructor on the object we're creating, because that object doesn't exist until after the constructor is called.
Your problem is that your declaration
pCustomParam = CustomParam
defines pCustomParam to be of type CustomParam, not a POINTER to Customparam.
Hence pointerToCustomParam will not be a POINTER, but a CustomParam - hence the 'rubbish contents'
Try
pCustomParam = ^CustomParam;

How can I use complex types referencing generic type

Consider the following types
type
TRecs = array[0..100000] of TRec;
PRecs = ^TRecs;
TRecObject = class
private
fRecs: PRecs;
public
constructor Create;
property Recs: PRecs read fRecs;
end;
I would like to make TRec a generic parameter. The problem is that I need to place outside of the class scope. Because something like
T<MyType>Object = class
private
fRecs: ^array[0..100000] of MyType;
public
property Recs: ^array[0..100000] of MyType read fRecs
end
is not possible.
Making PRecs a parameter also is not an option because there is TRec-related code in my actual object.
Is there a solution in modern Object Pascal? And if not, just curious is there any other generic-enabled language that can solve something like this?
I'm not entirely sure I understand your question, but I think you are looking for something like this:
type
TMyObject<T> = class
public
type
PArr = ^TArr;
TArr = array[0..100000] of T;
private
fRecs: PArr;
public
property Recs: PArr read fRecs
end;
That said, I can't see the point of that class. You could just use TList<T> from Generics.Collections.
And if your need an array, then you can use a dynamic array: TArray<T> or array of T, as you prefer.
You got your generic syntax a bit muddled up. Try this:
TRecArray<T> = array[0..100000] of T;
TGenericRecObject<T> = class
private
FRecs: TRecArray<T>;
public
property Recs: TRecArray<T> read FRecs;
end;

Resources