Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2014-02

How to use generics and meta class?

22 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

silvioprog

Hello,

I have this structure:

  generic TMyGeneric<T> = class
  end;
  TMyGenericClass = class of TMyGeneric;
  TMyType = class
  end;
  TMyClass1 = specialize TMyGeneric<TMyType>;
  TMyClass2 = class(specialize TMyGeneric<TMyType>)
  end;

...

procedure RegisterItem(AItemClass: TMyGenericClass);
begin
end;

OK, but when I try:

RegisterItem(TMyClass1);

Return:

Incompatible type for arg no. 1: Got "Class Of TMyGeneric$TMyType", expected "TMyGenericClass".

Or:

RegisterItem(TMyClass2);

Return:

Incompatible type for arg no. 1: Got "Class Of TMyClass2", expected "TMyGenericClass"

So, which syntax to use generic and meta class?

Thank you!

Silvio Clécio My public projects - github.com/silvioprog
Sven Barth
to silvioprog
silvioprog wrote 8 lines
Hello, I have this structure: generic TMyGeneric<T> = class end; TMyGenericClass = class of TMyGeneric;

That must not compile. This is a bug.

Show quoted text 34 lines
TMyType = class end; TMyClass1 = specialize TMyGeneric<TMyType>; TMyClass2 = class(specialize TMyGeneric<TMyType>) end; ... procedure RegisterItem(AItemClass: TMyGenericClass); begin end; OK, but when I try: RegisterItem(TMyClass1); Return: Incompatible type for arg no. 1: Got "Class Of TMyGeneric$TMyType", expected "TMyGenericClass". Or: RegisterItem(TMyClass2); Return: Incompatible type for arg no. 1: Got "Class Of TMyClass2", expected "TMyGenericClass" So, which syntax to use generic and meta class?

You can't. A generic is by definition not a fully existant type. You can only work with specializations.
E.g. TFPGList circumvents this by having the main implementation of the list inside a non generic TFPSList type which TFPGList derives from.

Regards,
Sven

Sven Barth
to silvioprog: I have this structure: generic TMyGeneric<T> = class end; TMyGenericClass = class of…
silvioprog wrote 42 lines
Hello, I have this structure: generic TMyGeneric<T> = class end; TMyGenericClass = class of TMyGeneric; TMyType = class end; TMyClass1 = specialize TMyGeneric<TMyType>; TMyClass2 = class(specialize TMyGeneric<TMyType>) end; ... procedure RegisterItem(AItemClass: TMyGenericClass); begin end; OK, but when I try: RegisterItem(TMyClass1); Return: Incompatible type for arg no. 1: Got "Class Of TMyGeneric$TMyType", expected "TMyGenericClass". Or: RegisterItem(TMyClass2); Return: Incompatible type for arg no. 1: Got "Class Of TMyClass2", expected "TMyGenericClass" So, which syntax to use generic and meta class?

What might work (untested) is this:

=== code begin ===

type
   generic TMyGeneric<T> = class
   public type
     TMyGenericClass = class of TMyGeneric; // in mode Delphi this would
be "class of TMyGeneric<T>"
   public
     class procedure RegisterItem(aItemClass: TMyGenericClass);
   end;

=== code end ===

Regards,
Sven

Sven Barth
to Sven Barth
Sven Barth wrote
Am 25.02.2014 00:27, schrieb silvioprog:

[snip]

> So, which syntax to use generic and meta class? What might work (untested) is this:

[snip]

Sorry for fully quoting the message. -.-

Regards,
Sven

silvioprog
to Sven Barth: That must not compile. This is a bug. You can't. A generic is by definition not a fully…

2014-02-25 5:14 GMT-03:00 Sven Barth :

Show quoted text 12 lines
Am 25.02.2014 00:27, schrieb silvioprog: > Hello, > > I have this structure: > > generic TMyGeneric<T> = class > end; > > TMyGenericClass = class of TMyGeneric; > That must not compile. This is a bug.

Can you issue it for me friend? I would be thankful. (y)

TMyType = class

Show quoted text 40 lines
> end; > > TMyClass1 = specialize TMyGeneric<TMyType>; > > TMyClass2 = class(specialize TMyGeneric<TMyType>) > end; > > ... > > procedure RegisterItem(AItemClass: TMyGenericClass); > begin > end; > > OK, but when I try: > > RegisterItem(TMyClass1); > > Return: > > Incompatible type for arg no. 1: Got "Class Of TMyGeneric$TMyType", > expected "TMyGenericClass". > > Or: > > RegisterItem(TMyClass2); > > Return: > > Incompatible type for arg no. 1: Got "Class Of TMyClass2", expected > "TMyGenericClass" > > So, which syntax to use generic and meta class? > You can't. A generic is by definition not a fully existant type. You can only work with specializations. E.g. TFPGList circumvents this by having the main implementation of the list inside a non generic TFPSList type which TFPGList derives from. Regards, Sven

Thank for this nice information, I didn't know that.

Silvio Clécio My public projects - github.com/silvioprog
silvioprog
to Sven Barth: What might work (untested) is this: === code begin === type generic TMyGeneric<T> = class…

2014-02-25 5:45 GMT-03:00 Sven Barth :
[...]

Show quoted text 22 lines
So, which syntax to use generic and meta class? > What might work (untested) is this: === code begin === type generic TMyGeneric<T> = class public type TMyGenericClass = class of TMyGeneric; // in mode Delphi this would be "class of TMyGeneric<T>" public class procedure RegisterItem(aItemClass: TMyGenericClass); end; === code end === Regards, Sven

Yyyeeesss buddy, it compiles! I'll continue my implementations this weekend. Generics is really wonderful! :)

I will continue my implementations this weekend.

Thanks a lot!

Silvio Clécio My public projects - github.com/silvioprog
silvioprog
to Sven Barth: [snip] [snip] Sorry for fully quoting the message. -.- Sven

2014-02-25 5:47 GMT-03:00 Sven Barth :

Show quoted text 18 lines
Am 25.02.2014 09:45, schrieb Sven Barth: Am 25.02.2014 00:27, schrieb silvioprog: > [snip] So, which syntax to use generic and meta class? >> > > What might work (untested) is this: > > [snip] Sorry for fully quoting the message. -.- Regards, Sven

No problems guy! :)

Silvio Clécio My public projects - github.com/silvioprog
Sven Barth
to silvioprog
silvioprog wrote 10 lines
2014-02-25 5:47 GMT-03:00 Sven Barth < >: Sorry for fully quoting the message. -.- Regards, Sven No problems guy! :)

You might have no problems with it, but there are some list subscribers that do have problems with it and it's also bad manner...

Regards,
Sven

silvioprog
to Sven Barth: What might work (untested) is this: === code begin === type generic TMyGeneric<T> = class…

2014-02-25 5:45 GMT-03:00 Sven Barth :
[...]

Show quoted text 22 lines
So, which syntax to use generic and meta class? > What might work (untested) is this: === code begin === type generic TMyGeneric<T> = class public type TMyGenericClass = class of TMyGeneric; // in mode Delphi this would be "class of TMyGeneric<T>" public class procedure RegisterItem(aItemClass: TMyGenericClass); end; === code end === Regards, Sven

Can I use this "metatype" in other class? E.g.:

  generic TMyGeneric<T> = class
  public type
    TMyGenericClass = class of TMyGeneric;
  end;
  TMyService = class
  public
    class procedure RegisterItem(aItemClass: TMyGenericClass); // maybe
"aItemClass: TMyGeneric.TMyGenericClass"?
  end;

I tried that but:

"Identifier not found "TMyGenericClass"".

Silvio Clécio My public projects - github.com/silvioprog
silvioprog
to silvioprog

Oops...

2014-02-26 12:16 GMT-03:00 silvioprog :
[...]

I tried that but: "Identifier not found "TMyGenericClass"".

Full test below:

  generic TMyGeneric<T> = class
  public type
    TMyGenericClass = class of TMyGeneric;
  end;
  TMyService = class
  public
    class procedure RegisterItem(aItemClass: TMyGeneric.TMyGenericClass);
  end;
  TMyClass = class(specialize TMyGeneric<TObject>)
  end;

..

begin
  TMyService.RegisterItem(TMyClass);
end;

Error: Incompatible type for arg no. 1: Got "Class Of TMyClass", expected "TMyGeneric.TMyGenericClass"

The only alternative is use "class procedure RegisterItem(aItemClass: TClass);" instead of "class procedure RegisterItem(aItemClass: *MyGenericType*);"?

Silvio Clécio My public projects - github.com/silvioprog
Sven Barth
to silvioprog: 2014-02-25 5:45 GMT-03:00 Sven Barth : [...] Can I use this "metatype" in other class?…
silvioprog wrote 13 lines
Can I use this "metatype" in other class? E.g.: generic TMyGeneric<T> = class public type TMyGenericClass = class of TMyGeneric; end; TMyService = class public class procedure RegisterItem(aItemClass: TMyGenericClass); // maybe "aItemClass: TMyGeneric.TMyGenericClass"? end;

You'd need something like "aItemClass: specialize
TMyGeneric<SpecificType>.TMyGenericClass", but that might not compile in mode ObjFPC.

As an aside: if TMyGeneric would not be a generic you'd need TMyGeneric.TMyGenericClass as well, a TMyGenericClass is part of TMyGeneric's scope and TMyService does not know this scope.

Regards,
Sven

Sven Barth
to silvioprog: 2014-02-25 5:45 GMT-03:00 Sven Barth : [...] Can I use this "metatype" in other class?…
silvioprog wrote
Can I use this "metatype" in other class? E.g.:

Just as a general remark: Outside of a generic's declaration or the implementation of its methods you *must not* (and *can not*) use it without "<...>" and the "..." needs to be a valid type (this includes type parameters if you use a generic inside the declaration or the implementation of another generic).

Regards,
Sven

Sven Barth
to Joao Morais
Joao Morais wrote 9 lines
Em 25/02/14 05:14, Sven Barth escreveu: > A generic is by definition not a fully existant type. Plans to include generics as a real type for class attributes? Something like the Java world: generic TMyGeneric<T: TMyType> <<-- this is the real type specialize TMyGeneric<TSomeDescendantType> <<-- only for type safety, discarded after compiling

No, because generics in FPC/Delphi work more like templates in C++ than generics in Java/.Net (in these two it works, because of their "everything is an object" approach, we don't have that in Pascal).

Regards,
Sven

Sven Barth
to Sven Barth
Sven Barth wrote 13 lines
Am 27.02.2014 12:12, schrieb Joao Morais: > Em 25/02/14 05:14, Sven Barth escreveu: >> A generic is by definition not a fully existant type. > > Plans to include generics as a real type for class attributes? > Something like the Java world: > > generic TMyGeneric<T: TMyType> <<-- this is the real type > specialize TMyGeneric<TSomeDescendantType> <<-- only for type > safety, discarded after compiling No, because generics in FPC/Delphi work more like templates in C++ than generics in Java/.Net (in these two it works, because of their "everything is an object" approach, we don't have that in Pascal).

Addendum: this does not exclude the compiler from trying to optimize this if the type parameter is indeed a class type. But this will be transparent for the user and will have a few restrictions. It does however not change that generics aren't full types.

Regards,
Sven

Joao Morais
to Sven Barth
Sven Barth wrote 14 lines
Am 27.02.2014 13:37, schrieb Sven Barth: > Am 27.02.2014 12:12, schrieb Joao Morais: >> Em 25/02/14 05:14, Sven Barth escreveu: >>> A generic is by definition not a fully existant type. >> >> Plans to include generics as a real type for class attributes? >> Something like the Java world: >> >> generic TMyGeneric<T: TMyType> <<-- this is the real type >> specialize TMyGeneric<TSomeDescendantType> <<-- only for type >> safety, discarded after compiling > No, because generics in FPC/Delphi work more like templates in C++ > than generics in Java/.Net (in these two it works, because of their > "everything is an object" approach, we don't have that in Pascal).

Correct. The feature I was talking about is something like:

generic TMyGeneric<T: TMyType>

And TMyType is a class. In this case the compiler knows I am talking about a generic for a class type and a "generic as Java" becomes possible.

Addendum: this does not exclude the compiler from trying to optimize this if the type parameter is indeed a class type. But this will be transparent for the user and will have a few restrictions. It does however not change that generics aren't full types.

:/

Maybe in the near (or not so near) future, these two approaches (templates and generics like in Java) may live together? I mean: is this technically possible?

luiz americo pereira camara
to Joao Morais

2014-02-27 18:43 GMT-03:00 Joao Morais :

Show quoted text 6 lines
Correct. The feature I was talking about is something like: generic TMyGeneric<T: TMyType> And TMyType is a class. In this case the compiler knows I am talking about a generic for a class type and a "generic as Java" becomes possible.

Seems that you are looking to class type constraints:

http://docwiki.embarcadero.com/RADStudio/XE4/en/Constraints_in_Generics http://blogs.teamb.com/craigstuntz/2008/08/29/37832

Not sure if will allows to do what you are looking for

Luiz

Sven Barth
to Joao Morais: Correct. The feature I was talking about is something like: generic TMyGeneric<T: TMyType>
Joao Morais wrote 22 lines
Em 27/02/14 09:39, Sven Barth escreveu: > Am 27.02.2014 13:37, schrieb Sven Barth: >> Am 27.02.2014 12:12, schrieb Joao Morais: >>> Em 25/02/14 05:14, Sven Barth escreveu: >>>> A generic is by definition not a fully existant type. >>> >>> Plans to include generics as a real type for class attributes? >>> Something like the Java world: >>> >>> generic TMyGeneric<T: TMyType> <<-- this is the real type >>> specialize TMyGeneric<TSomeDescendantType> <<-- only for type >>> safety, discarded after compiling >> No, because generics in FPC/Delphi work more like templates in C++ >> than generics in Java/.Net (in these two it works, because of their >> "everything is an object" approach, we don't have that in Pascal). Correct. The feature I was talking about is something like: generic TMyGeneric<T: TMyType> And TMyType is a class. In this case the compiler knows I am talking about a generic for a class type and a "generic as Java" becomes possible.

Even if the compiler chooses to optimize here this will be - as I said - transparent to the user. So it will not change that TMyGeneric is not a full type, because only TMyGeneric<SomeType> is a full type.

Also it will not always be possible to simplify (the restrictions I mentioned below): if the generic calls a non-virtual function of T than the compiler may not be able to optimize the generic implementation, because the concrete specialization type might implement the method as well (hiding the method of TMyType) and thus the two approaches would result in different calls.

See here:

=== code begin ===

type
   TMyType = class
     procedure Test;
   end;
   TMySubType = class(TMyType)
     procedure Test;
   end;
   generic TMyGeneric<T: TMyType> = class
     procedure Test;
   end;
procedure TMyType.Test;
begin
   Writeln('Foo');
end;
procedure TMySubType.Test;
begin
   Writeln('Bar');
end;
procedure TMyGeneric.Test;
var
   o: T;
begin
   o.Test;
end;
type
   TMyGenericMySubType = specialize TMyGeneric<TMySubType>;
var
   t: TMyGenericMySubType;
begin
   t.Test;
end.

=== code end ===

Currently this will print:

=== output begin ===

Bar

=== output end ===

If the compiler would now naively generate an implementation of TMyGeneric with TMyType then the result would be:

=== output begin ===

Foo

=== output end ===

This must not be. So the compiler - while it might generate an implicit TMyType implementation - must not use that implicit implemenation if the type that is specialized with implements non virtual methods itself. This is one of the restrictions.

Show quoted text 10 lines
> Addendum: this does not exclude the compiler from trying to optimize > this if the type parameter is indeed a class type. But this will be > transparent for the user and will have a few restrictions. It does > however not change that generics aren't full types. :/ Maybe in the near (or not so near) future, these two approaches (templates and generics like in Java) may live together? I mean: is this technically possible?

FPC/Delphi generics are more templates than generics. Generics are not full types. This will not change.

Regards,
Sven

Joao Morais
to luiz americo pereira camara: 2014-02-27 18:43 GMT-03:00 Joao Morais : Seems that you are looking to class type…
luiz americo pereira camara wrote 22 lines
2014-02-27 18:43 GMT-03:00 Joao Morais < >: Correct. The feature I was talking about is something like: generic TMyGeneric<T: TMyType> And TMyType is a class. In this case the compiler knows I am talking about a generic for a class type and a "generic as Java" becomes possible. Seems that you are looking to class type constraints: http://docwiki.embarcadero.com/RADStudio/XE4/en/Constraints_in_Generics http://blogs.teamb.com/craigstuntz/2008/08/29/37832 Not sure if will allows to do what you are looking for

Kind of. I usually want some type safety in a generic implementation. A couple of weeks ago I needed a generic type as an abstract class (changed the approach, don't remember details). Most of the time reintroduce some methods casting the inherited one solves the problem.

Thanks for the links! I didn't know the actual Delphi syntax =)

Joao Morais
to Sven Barth: Even if the compiler chooses to optimize here this will be - as I said - transparent to…
Sven Barth wrote 24 lines
=== code end === Currently this will print: === output begin === Bar === output end === If the compiler would now naively generate an implementation of TMyGeneric with TMyType then the result would be: === output begin === Foo === output end === This must not be. So the compiler - while it might generate an implicit TMyType implementation - must not use that implicit implemenation if the type that is specialized with implements non virtual methods itself. This is one of the restrictions.

Ah, now I see. "TGeneric<T: TType>" is quite different from "var T: TType". Afaics the former is a placeholder for another type, the latter is a placeholder for an implementation. Thanks for sharing.

Show quoted text 13 lines
>> Addendum: this does not exclude the compiler from trying to optimize >> this if the type parameter is indeed a class type. But this will be >> transparent for the user and will have a few restrictions. It does >> however not change that generics aren't full types. > > :/ > > Maybe in the near (or not so near) future, these two approaches > (templates and generics like in Java) may live together? I mean: is this > technically possible? FPC/Delphi generics are more templates than generics. Generics are not full types. This will not change.

Got the point. Anyway current (2.6) generics meets about 80% of my needs and 2.8 sounds to meet about 98% =). Thanks for the really great work with generics!

Sven Barth
to Joao Morais
"Joao Morais" wrote 15 lines
Em 28/02/14 03:44, Sven Barth escreveu: > > === code end === > > Currently this will print: > > === output begin === > > Bar > > === output end === > > If the compiler would now naively generate an implementation of

TMyGeneric with TMyType then the result would be:

Show quoted text 8 lines
> > === output begin === > > Foo > > === output end === > > This must not be. So the compiler - while it might generate an implicit

TMyType implementation - must not use that implicit implemenation if the type that is specialized with implements non virtual methods itself. This is one of the restrictions.

Ah, now I see. "TGeneric<T: TType>" is quite different from "var T:

TType". Afaics the former is a placeholder for another type, the latter is a placeholder for an implementation. Thanks for sharing.

Yes, that's an important difference :)

Show quoted text 16 lines
>>> Addendum: this does not exclude the compiler from trying to optimize >>> this if the type parameter is indeed a class type. But this will be >>> transparent for the user and will have a few restrictions. It does >>> however not change that generics aren't full types. >> >> >> :/ >> >> Maybe in the near (or not so near) future, these two approaches >> (templates and generics like in Java) may live together? I mean: is this >> technically possible? > > > FPC/Delphi generics are more templates than generics. Generics are not

full types. This will not change.

Got the point. Anyway current (2.6) generics meets about 80% of my needs

and 2.8 sounds to meet about 98% =). Thanks for the really great work with generics!

You're welcome. I still have enough work left though ;)

Regards,
Sven