skip generic parameter on demand - delphi

I found this definition of an generic class in the web :
TVertex<T> = class
public
Name: String;
OutputAttributes: TVertexOutputAttributes;
Marker: Boolean;
Data: T; // User-defined data attribute
function HasAdditionalAttributes: Boolean;
constructor Create();
procedure Mark;
procedure UnMark;
end;
a single instance of a TVertex class is created with the following line of code :
A := TVertex<Integer>.Create();
A.Name := 'A';
in this example we define T as Integer data Type. My question goes now like this :
If my use case does not need any assigend datatype T it would be much better / logical if I can skip the specification on the datatype. I failed with :
A := TVertex<>.Create();
A.Name := 'A';
Any change to avoid the handover of the datatype at the create process ???

What you are explicitly asking for has one obvious flaw. What will the compiler do with this declaration?
Data: T; // User-defined data attribute
If you don't supply a T, then the compiler doesn't know what to do.
This observation leads us to one possible approach. If you don't want to supply T then presumably you don't want the class to contain this member. How can it contain that member if its type is not supplied? So define a non-generic version:
type
TVertex = class
public
Name: String;
OutputAttributes: TVertexOutputAttributes;
Marker: Boolean;
end;
And then derive your generic version from this:
type
TVertex<T> = class(TVertex)
public
Data: T; // User-defined data attribute
end;
Clearly you would need to determine in which class the methods should be declared and implemented. Clearly any methods that did not rely on Data could be implemented in the non-generic class.

Related

How to use lazy loading using DAO or other concept

I am use DAO for load object.
But the object is complex and in different places of the program only certain parts of this object are needed.
There is a need for lazy loading, but it is not clear where to place it.
I would like something similar to include entity framework
Options that come to my mind:
in the model object (when accessing a property or a specific method)
but in this case, it is necessary to have a DAO inside the object
this gives a dependence on a specific type of DAO
in DAO (specific method or include().include() chain)
but in this case it is necessary to extend the DAO interface with a strange "include" method
or create many DAO combinations, for example: "docWithTab1AndTab2", "docWithTab2WithOutTab1" and etc
combinatorial explosion...
if the DAO is responsible for interacting with the data source, then it turns out that it should do lazy loading, then where should the code be located?
rough example:
GenericDao<T: class> = interface
function find(const id: string): T;
procedure save(entity: T);
function update(entity: T): T;
procedure delete(entity: T);
function findAll(): TList<T>;
end;
TUser = class
Id: string;
Name: string;
Adress: TList<TAdress>;
Phone: TList<TPhone>;
...
etc TList<> field
end;
TUserFirebirdDao = class(TInterfacedObject, GenericDao<TUser>)
function find(const id: string): TUser;
end;
function TUserFirebirdDao.find(const id: string): TUser;
begin
Result := TUser.Create();
TFDQuery.Open('select * from users where id = :id');
...
further filling properties from the dataset
...
end;
I get filled object, but I need more "Adress" and possibly "Phone".
You don't need to download them right away.
What to do?
TUser.GetPhone(): TList<TPhone>
begin
TPhoneFirebirdDao.find() ???
end;
or
TUserWithPhoneFirebirdDao = class
TUserWithAdressFirebirdDao = class
TUserWithPhoneAndAdressFirebirdDao = class
Or use some other approach, concept?
Please answer as clear as possible.
Thank you.

How to assign an empty set to a record using overloaded operators

I'm using a record to encapsulate two dissimular sets.
I've put in operators to allow the assignment of either set to the record. Doing so will clear the other set.
However I cannot assign an empty set.
See the following example code:
Program test;
{$Apptype console}
type
TSomeThing = (a,b,c);
TOtherThing = (x,y,z);
TSomeThings = set of TSomething;
TOtherThings = set of TOtherThing;
TSomeRecord = record
strict private
Fa: TSomeThings;
Fb: TOtherThings;
public
class operator Implicit(a: TSomeThings): TSomeRecord;
class operator Implicit(a: TOtherThings): TSomeRecord;
end;
implementation
class operator TSomeRecord.Implicit(a: TSomeThings): TSomeRecord;
begin
Result.Fa:= a;
Result.Fb:= [];
end;
class operator TSomeRecord.Implicit(a: TOtherThings): TSomeRecord;
begin
Result.Fa:= [];
Result.Fb:= a;
end;
var
SomeRec: TSomeRecord;
begin
SomeRec:= [];
end.
[dcc64 Error] InstructionList.pas(512): E2010 Incompatible types: 'TSomeRecord' and 'Set'
How do I make it so I can assign the empty set to my record?
I can misuse the implicit operator to allow SomeRec:= nil;, but that looks very ugly.
The compiler cannot tell whether you mean an empty set of TSomeThing or an empty set of TOtherThing. You can declare typed constants to allow the compiler to resolve the overload:
const
EmptySomeThings: TSomeThings = [];
EmptyOtherThings: TOtherThings = [];
Then the following assignments compile and resolve as you would expect:
SomeRec:= EmptySomeThings;
SomeRec:= EmptyOtherThings;
Of course, you know that either one of these has the same effect, because the implementation of the Implicit operators sets one field, and clears the other. But the compiler cannot know this.
If you wish to clear both members of the record you can always use:
SomeRec:= Default(TSomeRecord);
I personally might wrap that up in a static class method like this:
class function Default: TSomeRecord; static;
....
class function TSomeRecord.Default: TSomeRecord;
begin
Result := Default(TSomeRecord);
end;
Then you can write:
SomeRec:= TSomeRecord.Default;
In an ideal world you'd be able to declare a constant in the type, but the language designers did not think of that and it is sadly not possible.
Update
Rudy correctly points out in a comment, that constants can be added to a record type by way of a record helper. This was news to me as I mistakenly believed that helpers could only add methods. This is what I love about Stack Overflow. Even when you think you know something pretty well, there's always scope for more knowledge to be acquired. Thanks Rudy.
So you might write:
type
TSomeRecordHelper = record helper for TSomeRecord
public
const
Default: TSomeRecord = ();
end;

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

Delphi generics: How to spec "class that references its own type"

In Delphi XE2, I want to write a generic collection class which manipulates objects which must have a Copy(owntype) method, but I can't figure out how best to declare this.
I want something like this example (a collection of one item, for simplicity):
//------ Library ------
Type
TBaseCopyable = class
S: string;
// procedure Copy(OtherObject: TBaseCopyable); overload;
procedure Copy(OtherObject: TBaseCopyable); virtual;
end;
MyCollection<T: TBaseCopyable, constructor> = class
TheItem: T;
procedure SetItem(AItem: T);
function GetItem: T;
end;
[...]
function MyCollection<T>.GetItem: T;
Var
NewItem: T;
begin
NewItem := T.Create;
NewItem.Copy(TheItem);
Result := NewItem;
end;
//------ Usage ------
Type
TMyCopyable = class(TBaseCopyable)
I: integer;
// procedure Copy(OtherObject: TMyCopyable); overload;
procedure Copy(OtherObject: TMyCopyable); override;
end;
[...]
Col: MyCollection<TMyCopyable>;
The key problem is that in Col, I need the generic implementation of MyCollection to find TMyCopyable.Copy. Unsurprisingly, neither overload or virtual do the job:
With overload, the code compiles, but MyCollection.GetItem finds
TBaseCopyable.Copy, not TMyCopyable.Copy.
With virtual/override this
doesn't compile because the signatures of the two Copy declarations
don't match.
So I figure I need to use generics somehow in the specification of TBaseCopyable, possibly instead of inheritance. But I'm not sure how, primarily because I don't particularly need to feed a type parameter into TBaseCopyable, I just need the Copy argument type to refer to "the type of it's own class" in a generic way.
Ideas? Thanks!
Turn TBaseCopyable into a Generic class and apply its Generic type to Copy(), then TMyCopyable can override it, eg:
type
TBaseCopyable<T> = class
S: string;
procedure Copy(OtherObject: T); virtual;
end;
MyCollection<T: TBaseCopyable<T>, constructor> = class
TheItem: T;
procedure SetItem(AItem: T);
function GetItem: T;
end;
type
TMyCopyable = class(TBaseCopyable<TMyCopyable>)
I: integer;
procedure Copy(OtherObject: TMyCopyable); override;
end;
Alternatively, just do the same thing that TPersistent.Assign() does (since it does not use Generics):
type
TBaseCopyable = class
S: string;
procedure Copy(OtherObject: TBaseCopyable); virtual;
end;
MyCollection<T: TBaseCopyable, constructor> = class
TheItem: T;
procedure SetItem(AItem: T);
function GetItem: T;
end;
type
TMyCopyable = class(TBaseCopyable)
I: integer;
procedure Copy(OtherObject: TBaseCopyable); override;
end;
procedure TMyCopyable.Copy(OtherObject: TBaseCopyable);
begin
inherited;
if OtherObject is TMyCopyable then
I := TMyCopyable(OtherObject).I;
end;
Answering my own question, or at least summarizing findings:
So far as I can tell, there is no complete answer to the question as I posed it. What I have learned is this:
[1] Remy's solution is the way to go if the base item class (here TBaseCopyable) has no state, and either is abstract, or methods don't need to refer to other objects of the same type. (Eg: TBaseCopyable would have no fields and only abstract methods.)
[2] A significant issue is how to specify a generic class whose descendant classes can specify method arguments and return values of the same type as their enclosing class. In Remy's example, that is accomplished in the descendant class declaration:
TMyCopyable = class(TBaseCopyable<TMyCopyable>)
This means that in the generic class, T will be replaced by the ultimate class of interest.
[3] However, within TBaseCopyable's generic declaration, the information that T is always a TBaseCopyable is not available, so in TBaseCopyable's implementation, references to objects of type T won't be able to see TBaseCopyable's methods or fields.
This would be solved if we could set a constraint on T to tell the compiler that T is a TBaseCopyable.
That's apparently the approach in C#:
http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx
In Delphi, I think that would go like this:
type
TBaseCopyable<T: TBaseCopyable<T> > = class
...
like Remy shows for MyCollection. However, that syntax is not legal within the same class declaration (error: undeclared identifier TBaseCopyable), because TBaseCopyable is not yet fully defined. We might think to create a forward declaration for TBaseCopyable (like we would for non-generic classes), but that throws an error, and apparently it's not supported by the compiler:
How to set a forward declaration with generic types under Delphi 2010?
E2086 error with forward declaration of a generic type http://qc.embarcadero.com/wc/qcmain.aspx?d=94044
[4] Maybe the generic class could inherit the implementation?
What if we did this:
type
TBaseCopyable<T> = class(TBaseCopyableImpl) ...
That would allow TBaseCopyable to have some fields and methods that could refer to each other. However, even if those methods were virtual, they would impose fixed argument/return types on the descendants, the avoidance of which was the rationale for using generics in the first place.
So this strategy is only good for fields and methods that don't need to specialize in the descendant types... for example an object counter.
Conclusions
This question turns out to concern the somewhat-known "Curiously recurring template pattern": http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern. Even though it seems what one is trying to accomplish is simple, there are theoretical problems behind the scenes.
The situation appears to call for a language keyword meaning something like "Same type as my enclosing class". However that apparently leads to covariance/contravariance issues -- violations of the rules of which types can substitute for which in inheritance hierachies. That said, it seems Delphi doesn't go as far as C# to permit as much of a partial solution.
Of course, I'd be happy to learn that there is a way to go further!
Oh, and I don't feel too bad struggling to get to the bottom of this -- even Ken Arnold thinks it's difficult: https://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid.html#comment-828994
:-)

Making a linked-list with generics

I have read how to make a pointer to a normal class and use it inside the class difinition:
type
PExample = ^TExample;
TExample = class
data: Integer;
next: PExample;
end;
but how do you do it with templatized parameters? This does not compile with the error Undeclared identifier: 'TExample' on the second line:
type
PExample = ^TExample;
TExample<T> = class
data: T;
next: PExample;
end;
Changing it to
PExample = ^TExample<T>;
does not fix it.
As you are using a class, you don't need to use a PExample. Classes are already reference types.
TExample<T> = class
data: T;
next: TExample<T>;
end;
This should work without the need to declare any pointer types. Pointer types are only required if you work with records (which are value types).
EDIT:
To my surprise I just noticed that this compiles and works in Delphi XE:
program Project;
{$APPTYPE CONSOLE}
type
TNode<T> = record
Next: ^TNode<T>;
Data: T;
end;
var
Node1, Node2: TNode<Integer>;
begin
Node1.Next := #Node2;
Node1.Data := 1;
Node2.Next := nil;
Node2.Data := 2;
WriteLn(Node1.Data);
WriteLn(Node1.Next.Data);
end.
It does still not solve the problem of defining a general generic pointer type, because this:
PNode<T> = ^TNode<T>;
does not work.
The other answers tell you how to build a generic linked list of classes. If you ever need to build a generic linked list of records, you cannot do so at present:
type
PNode<T> = ^TNode<T>;
TNode<T> = record
public
Next: PNode;
end;
does not compile.
Nor does:
type
TNode<T> = record
type
PNode = ^TNode;
public
Next: PNode;
end;
I believe that the reason for this is that the single-pass compiler does not support forward declarations of methods. This is actually more of a practical problem for operator overloading than for generics because classes can be used for generic linked lists.
Is it necessary to use a pointer to a class? Using a class reference for your "next" field should work, like this:
type
TExample<T> = class
data: T;
next: TExample<T>;
end;
The "next" field can still be NIL or can be assigned another TExample instance. This seems to negate the need of using a traditional ^ pointer (although you could argue that a reference to a class is also a pointer, just with different syntax).

Resources