* * *

Author Topic: Strange compiler behavior with generics [BUG?]  (Read 2726 times)

soerensen3

  • New member
  • *
  • Posts: 41
Re: Strange compiler behavior with generics [BUG?]
« Reply #15 on: June 22, 2017, 10:33:59 pm »
@Thaddy:

CParent is a parameter not an instance and I already changed the syntax to delphi mode which is in this case deleting the specialize and generic keywords. Could you please have a look at the attachment in the bug post again. I'm sure you will see that this is not an instance.
In case the colon in the generic parameter is your problem. If a generic parameter is followed by a colon the following type works as a constraint. This means the base type is at least of this class type (If you use a class). See this for more detail: http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Constraints_in_Generics
Code: Pascal  [Select]
  1. type
  2.   TTest
  3.     < Type: TObject>  // TObject is a constraint here
  4.     class
  5.   end;
  6.  
See the section about constaints http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Constraints_in_Generics

Lazarus 1.8 with FPC 3.0.2
Target: Fedora Linux 64 Bit (korora 4.13.4-200.fc26.x86_64)

Thaddy

  • Hero Member
  • *****
  • Posts: 4651
Re: Strange compiler behavior with generics [BUG?]
« Reply #16 on: June 22, 2017, 10:57:16 pm »
First mistake: type is a reserved word... Don't be THAT stupid. Class too. I happen to know Pascal ignores white space... too. And you missed "=". Oh well.
Checkout your code first. <this is not grumpy but angry  >:D >:D >:D>
I already did my best to explain to you how to do that. My first code even uses a constraint properly: Tpersistent. And the second example I gave also constrains to TObject by using class. Now stop it. >:( >:(

Try your code in Delphi . Does not work..... >:D >:D
Try my code in Delphi: works. O:-) I wonder why that is ?

And don't refer to documentation before you actually understand it, which you don't at the moment.
« Last Edit: June 22, 2017, 11:07:01 pm by Thaddy »
"Logically, no number of positive outcomes at the level of experimental testing can confirm a scientific theory, but a single counterexample is logically decisive."

soerensen3

  • New member
  • *
  • Posts: 41
Re: Strange compiler behavior with generics [BUG?]
« Reply #17 on: June 23, 2017, 02:03:28 am »
Just to show that it is possible to do something like multiple inheritance with generics run this code and have a look at the output.
Code: Pascal  [Select]
  1. program Project1;
  2. {$APPTYPE CONSOLE}
  3. {$mode delphi}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Classes
  10.   { you can add units after this };
  11.  
  12. type
  13.  
  14.   { TShowClassParents }
  15.  
  16.   TShowClassParents < T: class > = class ( T )  //generic that shows class parents
  17.     procedure WriteClassParents;
  18.   end;
  19.  
  20.   { TNamedObject }
  21.  
  22.   TNamedObject < T: class > = class ( T ) //generic with a name field
  23.     private
  24.       FName: String;
  25.  
  26.     public
  27.       property Name: String read FName write FName;
  28.   end;
  29.  
  30.  
  31. procedure TShowClassParents < T >.WriteClassParents;
  32. var
  33.   c: TClass;
  34. begin
  35.   c:= ClassType;
  36.   while Assigned ( C ) do // This will loop through all class parents and write them to the output
  37.     begin
  38.       WriteLn( C.ClassName );
  39.       c:= c.ClassParent;
  40.     end;
  41. end;
  42.  
  43. type
  44.   //predeclare types as specializations cannot be passed as a parameter directly
  45.   TListShowParents = TShowClassParents < TList >; //A list with the WriteClassParents method
  46.   TNamedList = TNamedObject < TList >; //A List with a name field
  47.  
  48. var
  49.   Ob: TShowClassParents < TPersistent >; // A persistent with the WriteClassParents method
  50.   Ob2: TNamedObject< TListShowParents >; // "Multiple inherit" first inherit TListShowParent then inherit TNamedObject
  51.   Ob3: TListShowParents< TNamedList >; // Do it the other way around
  52.  
  53. begin
  54.   WriteLn( 'Class parents of Ob' );
  55.   Ob:= TShowClassParents < TPersistent >.Create;
  56.   Ob.WriteClassParents;
  57.   Ob.Free;
  58.  
  59.   WriteLn;
  60.  
  61.   WriteLn( 'Class parents of Ob2' );
  62.   Ob2:= TNamedObject < TListShowParents >.Create;
  63.   Ob2.WriteClassParents;
  64.   Ob2.Free;
  65.  
  66.   WriteLn;
  67.  
  68.   WriteLn( 'Class parents of Ob3' );
  69.   Ob3:= TShowClassParents < TNamedList >.Create;
  70.   Ob3.WriteClassParents;
  71.   Ob3.Free;
  72. end.
  73.  

You can see that this is possible in Free Pascal even with Delphi Mode. I don't have Delphi so I cannot test it there.

But in a generic like this I cannot inherit virtual methods from a base class. I think there is the compiler bug. That it complaints that the base class does not have this method at all while in the above case where no method is overridden it just compiles.
Lazarus 1.8 with FPC 3.0.2
Target: Fedora Linux 64 Bit (korora 4.13.4-200.fc26.x86_64)

molly

  • Hero Member
  • *****
  • Posts: 1963
Re: Strange compiler behavior with generics [BUG?]
« Reply #18 on: June 24, 2017, 01:40:38 am »
According to Delphi wiki:
Quote
The base type of a parameterized class or interface type might be an actual type or a constructed type. The base type might not be a type parameter alone.

Whether or not the compiler should complain about that for your example code, i would have no idea (my personal opinion would be, yes the compiler should complain on faulty code).

The type definition for FPC can be a bit ambiquitos in that regards...

imho, another flaw with regards to your example is that you expect the generic to have a base type of class, while the compiler wouldn't know that from your declaration alone. Only when specialization comes into play the base type becomes clear.

You could just as well meant for your generic template declaration to be a record.

Your idea on how a generic should be declared seems a bit strange to me, and i was unable to find any example code (either from Delphi or FPC) that tries to use (only) a template for its base type.

Because the base type is unknown/uncertain, then how would the compiler know which methods it inherits and can be overridden or not ?
« Last Edit: June 24, 2017, 02:33:07 am by molly »

soerensen3

  • New member
  • *
  • Posts: 41
Re: Strange compiler behavior with generics [BUG?]
« Reply #19 on: June 24, 2017, 10:00:44 pm »
ok, so you say the class constraint will also accept record types? I didn't know that. But I even tried TObject or TPersistent for a constraint, but it didn't help. I thought that the type checks for a generic were done in each specialization because it also cannot know the types of other variables which the compiler needs to know for the method bodies.

But on the other hand generics are made for reducing redundant code, so I think it makes sense to program some basic behavior and apply it to different classes instead of copying code to many classes (and also maintain).
While Thaddy's solution also solves this problem it feels more natural to me to call for example
Code: Pascal  [Select]
  1. Obj1.Name:= 'SomeName';
instead of
Code: Pascal  [Select]
  1. Obj1.SomeProp.Name:= 'SomeName';
but the advantage would be that you could add behaviors on runtime (without the generic part).
Lazarus 1.8 with FPC 3.0.2
Target: Fedora Linux 64 Bit (korora 4.13.4-200.fc26.x86_64)

molly

  • Hero Member
  • *****
  • Posts: 1963
Re: Strange compiler behavior with generics [BUG?]
« Reply #20 on: June 25, 2017, 01:11:46 am »
Let me first start by saying that generics is a bit out of my comfort-zone.

I know the basics and use them as such but it can drive a person insane trying to grasp what code is actually produced when using generics that inherited from other generics , combining generics with or without interfaces (which can also be made out of generics).

Quote
ok, so you say the class constraint will also accept record types?
No, not exactly (although i realize now, that it could perhaps be interpreted as such).

I meant the opposite, because of what happens when you use a template for base type (again, note that this should not be allowed). But indulging your train of thought, the compiler would have no clue how to handle things until it reaches the specialization.

consider the following:

Code: [Select]
type
  TSomeRecord = record
    SomeField: Integer;
  end;

  generic gTest2<T: TObject> = class(T)
  end;

  TG2a = specialize gTest2<TPersistent>;
  TG2b = specialize gTest2<TSomeRecord>;

And compilations of TGB2b specialization fails, for obvious reasons.

Quote
I thought that the type checks for a generic were done in each specialization because it also cannot know the types of other variables which the compiler needs to know for the method bodies.
I am not going to pretend that i know how the compiler actually solves things, but as far as i understood the base type must be declared before.

Which brings us to the question whether or not the compilation of TG2a specialization should be at all possible or not (or if  the compiler should already complain at the generic declaration).

Hence why i linked to the official type declaration for FPC generics, as that leaves room for such construct to be allowed (while Delphi seems to state things more explicitly in its documentation).

Quote
While Thaddy's solution also solves this problem it feels more natural to me to call for example ...
And imho that depends on what it is that you are actually trying to achieve, since you seem to have dismissed all other suggestion that were made in this thread.

In basics (without looking any further) you seem to wish to inherit from another class and add new properties, something that btw can also be achieved by using a helper class. (but i realize that is not what you meant).

So then it's starting to look more as if you expect templates to be a full blown form of macro's that are resolved at compile time and which are solved recursively.

In that case, i suspect that you are trying to do too many things at once and try to wrap things up in a simple one/two-liner, but which would perhaps better be solved by using several small in between steps to achieve the desired result.

But again, that entirely depends on what it is that you are actually trying to achieve. The simple examples that you have shown so far are not going to answer that.

afaik not many (compiler) developers are paying close attention to these forums, so therefor you might perhaps have more luck on posting your question on the mailing list (if you haven't done so already). That could perhaps also result in better suited answers (at least better than the ones that i am able to produce).
« Last Edit: June 25, 2017, 01:14:57 am by molly »

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus