Recent

Author Topic: [Solved] Generic record enumerator in Delphi mode  (Read 13246 times)

Okoba

  • Hero Member
  • *****
  • Posts: 528
[Solved] Generic record enumerator in Delphi mode
« on: June 16, 2021, 04:04:22 pm »
I am trying to write this FPC mode code in the Delphi mode, mostly because I do not like to write generic and specialize in every place and in a real world sample it will a lot and code will look somewhat ugly.
Code: Pascal  [Select][+][-]
  1. unit Test3;
  2.  
  3. {$mode ObjFPC}{$H+}
  4. {$modeswitch advancedrecords}
  5.  
  6. interface
  7.  
  8. type
  9.   generic TTest3Enumerator<T> = record
  10.     FValue: Pointer;
  11.     FCurrent: Integer;
  12.     function GetCurrent: T;
  13.     function MoveNext: Boolean;
  14.     property Current: T read GetCurrent;
  15.   end;
  16.  
  17.   generic TTest3<T> = record
  18.   type
  19.     TTest3EnumeratorSpec = specialize TTest3Enumerator<T>;
  20.   var
  21.     Value: array of T;
  22.     function GetEnumerator: TTest3EnumeratorSpec;
  23.   end;
  24.  
  25. implementation
  26.  
  27. function TTest3.GetEnumerator: TTest3EnumeratorSpec;
  28. begin
  29.   Result.FCurrent := -1;
  30.   Result.FValue := @Self;
  31. end;
  32.  
  33. function TTest3Enumerator.GetCurrent: T;
  34. begin
  35.   Result := specialize TTest3<T>(FValue^).Value[FCurrent];
  36. end;
  37.  
  38. function TTest3Enumerator.MoveNext: Boolean;
  39. begin
  40.   FCurrent := FCurrent + 1;
  41.   Result := FCurrent < Length(specialize TTest3<T>(FValue^).Value);
  42. end;
  43.  
  44. end.

The code in Delphi mode:

Code: Pascal  [Select][+][-]
  1. unit Test5;
  2.  
  3. {$mode Delphi}
  4.  
  5. interface
  6.  
  7. type
  8.   TTest5Enumerator<T> = record
  9.     FValue: Pointer;
  10.     FCurrent: Integer;
  11.     function GetCurrent: T;
  12.     function MoveNext: Boolean;
  13.     property Current: T read GetCurrent;
  14.   end;
  15.  
  16.   TTest5<T> = record
  17.   type
  18.     TTest5EnumeratorSpec = TTest5Enumerator<T>;
  19.   var
  20.     Value: array of T;
  21.     function GetEnumerator: TTest5EnumeratorSpec;
  22.   end;
  23.  
  24. implementation
  25.  
  26. function TTest5.GetEnumerator: TTest5EnumeratorSpec; //<<Error
  27. begin
  28.   Result.FCurrent := -1;
  29.   Result.FValue := @Self;
  30. end;
  31.  
  32. function TTest5Enumerator.GetCurrent: T;
  33. begin
  34.   Result := TTest5<T>(FValue^).Value[FCurrent];
  35. end;
  36.  
  37. function TTest5Enumerator.MoveNext: Boolean;
  38. begin
  39.   FCurrent := FCurrent + 1;
  40.   Result := FCurrent < Length(TTest5<T>(FValue^).Value);
  41. end;
  42.  
  43. end.

And the error :
Quote
test5.pas(26,10) Error: Class identifier expected

Suggestion about why the error?
« Last Edit: June 20, 2021, 11:14:56 pm by OkobaPatino »

avk

  • Hero Member
  • *****
  • Posts: 752
Re: Generic record enumerator in Delphi mode
« Reply #1 on: June 16, 2021, 04:21:26 pm »
It seems there should be something like this:
Code: Pascal  [Select][+][-]
  1. type
  2.   TTest5Enumerator<T> = record
  3.     FValue: Pointer;
  4.     FCurrent: Integer;
  5.     function GetCurrent: T;
  6.     function MoveNext: Boolean;
  7.     property Current: T read GetCurrent;
  8.   end;
  9.  
  10.   TTest5<T> = record
  11.   type
  12.     TTest5EnumeratorSpec = TTest5Enumerator<T>;
  13.   var
  14.     Value: array of T;
  15.     function GetEnumerator: TTest5EnumeratorSpec;
  16.   end;
  17.  
  18. implementation
  19.  
  20. function TTest5<T>.GetEnumerator: TTest5EnumeratorSpec;
  21. begin
  22.   Result.FCurrent := -1;
  23.   Result.FValue := @Self;
  24. end;
  25.  
  26. function TTest5Enumerator<T>.GetCurrent: T;
  27. begin
  28.   Result := TTest5<T>(FValue^).Value[FCurrent];
  29. end;
  30.  
  31. function TTest5Enumerator<T>.MoveNext: Boolean;
  32. begin
  33.   FCurrent := FCurrent + 1;
  34.   Result := FCurrent < Length(TTest5<T>(FValue^).Value);
  35. end;  
  36.  

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: Generic record enumerator in Delphi mode
« Reply #2 on: June 17, 2021, 01:47:53 pm »
Delphi supports type overloads for generics (e.g. TFoo<T> and TFoo<S, T> both inside the same unit). Thus you need to use that as well when providing a method implementation as avk showed (e.g. procedure TTest5Enumerator<T>.GetCurrent: T and so on).

Okoba

  • Hero Member
  • *****
  • Posts: 528
Re: Generic record enumerator in Delphi mode
« Reply #3 on: June 17, 2021, 02:39:13 pm »
Thanks to both of you.
@PascalDragon, is there any plan to allow this simple style of Generic in Delphi or a compiler condition to activate it? I like FPC mode much more (eg AutoDeref-) but generics makes me use Delphi mode and change almost all things back to FPC mode.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11351
  • FPC developer.
Re: Generic record enumerator in Delphi mode
« Reply #4 on: June 17, 2021, 03:00:12 pm »
much more (eg AutoDeref-) 

use
{$mode delphi}{$pointermath on}

Okoba

  • Hero Member
  • *****
  • Posts: 528
Re: Generic record enumerator in Delphi mode
« Reply #5 on: June 17, 2021, 05:06:35 pm »
I meant that FPC mode has a better defaults like AutoDeref, but the generic defaults seems wordy. Actually I am using pointermath option and many more with Delphi mode to simulate FPC mode with Delphi mode generic.
If there was a option for generics too it made the life easier. Or supporting the simple syntax in FPC mode.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: Generic record enumerator in Delphi mode
« Reply #6 on: June 18, 2021, 07:36:37 am »
Thanks to both of you.
@PascalDragon, is there any plan to allow this simple style of Generic in Delphi or a compiler condition to activate it? I like FPC mode much more (eg AutoDeref-) but generics makes me use Delphi mode and change almost all things back to FPC mode.

No. The purpose of mode Delphi is to be able to compile Delphi code. Delphi code will contain the type parameters. Thus it makes no sense to leave them out.

In fact my plan is to allow type overloads in non-Delphi modes as well, thus the method definitions will require the type parameters as well if type overloading is in fact used for that type.

I meant that FPC mode has a better defaults like AutoDeref, but the generic defaults seems wordy. Actually I am using pointermath option and many more with Delphi mode to simulate FPC mode with Delphi mode generic.
If there was a option for generics too it made the life easier. Or supporting the simple syntax in FPC mode.

The generic syntax might seem simple to you, but for the compiler it's a hell to parse. Not all expressions you can use in non-Delphi modes can be used in Delphi mode currently (especially when generic functions/methods are involved).

Okoba

  • Hero Member
  • *****
  • Posts: 528
Re: Generic record enumerator in Delphi mode
« Reply #7 on: June 18, 2021, 08:17:25 pm »
The generic syntax might seem simple to you, but for the compiler it's a hell to parse. Not all expressions you can use in non-Delphi modes can be used in Delphi mode currently (especially when generic functions/methods are involved).
Can you give an example of what can not be used in Delphi mode currently?

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: Generic record enumerator in Delphi mode
« Reply #8 on: June 19, 2021, 03:52:07 pm »
The generic syntax might seem simple to you, but for the compiler it's a hell to parse. Not all expressions you can use in non-Delphi modes can be used in Delphi mode currently (especially when generic functions/methods are involved).
Can you give an example of what can not be used in Delphi mode currently?

A primitive, essentially useless example, but it should get the point across:

Code: Pascal  [Select][+][-]
  1. program tgenfunc;
  2.  
  3. {$ifdef USE_MODE_DELPHI}
  4. {$mode delphi}
  5. {$else}
  6. {$mode objfpc}
  7. {$endif}
  8.  
  9. {$ifndef USE_MODE_DELPHI}generic{$endif} function GetValue<T>(aArg: T): T;
  10. begin
  11.   Result := aArg;
  12. end;
  13.  
  14. var
  15.   a: LongInt;
  16. begin
  17.   a := {$ifndef USE_MODE_DELPHI}specialize{$endif} GetValue<LongInt>(42) +
  18.          {$ifndef USE_MODE_DELPHI}specialize{$endif} GetValue<LongInt>(21);
  19. end.

This will compile if you don't specify USE_MODE_DELPHI, but will fail if you do (in Delphi it will compile however).

Okoba

  • Hero Member
  • *****
  • Posts: 528
Re: Generic record enumerator in Delphi mode
« Reply #9 on: June 19, 2021, 08:42:33 pm »
Thank you for the example. Actually I am hitting this but I was thinking it is a generic limitation for now, not the Delphi mode and I am using temp variable as a workaround.
Some questions:
  • Is there a issue for this so I can follow up? (I can not find any issues for such topic)
  • Will it be fixed with the implicit generic specialization Ryan and you are working on? (I am following the issue and development)
  • Is there or will there be a compiler option to use the Delphi mode generic with FPC mode without leaving the FPC mode?
  • Can I ask what is your idea about what is the better option in the long run? The Delphi generic mode or the FPC one?
I am interested in using generics but I am not a compiler developer so your opinion will help me see the picture more clearly.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: Generic record enumerator in Delphi mode
« Reply #10 on: June 20, 2021, 11:43:05 am »
  • Is there a issue for this so I can follow up? (I can not find any issues for such topic)

No.

  • Will it be fixed with the implicit generic specialization Ryan and you are working on? (I am following the issue and development)

At least for those cases for which the implicit specialization would indeed work, yes. (Side note: in case of an implicit specialization the specialize keyword also isn't required in non-Delphi modes, in fact it's not even allowed, because specialize and <…> belong together)

It would however not work if you use types, cause those don't take part in implicit specialization (adjusted example from above):

Code: Pascal  [Select][+][-]
  1. program tgenfunc;
  2.  
  3. {$ifdef USE_MODE_DELPHI}
  4. {$mode delphi}
  5. {$else}
  6. {$mode objfpc}
  7. {$endif}
  8.  
  9. type
  10.   {$ifndef USE_MODE_DELPHI}generic{$endif} TTest<T> = class
  11.     class function GetValue(aArg: T): T;
  12.   end;
  13.  
  14. class function TTest{$ifdef USE_MODE_DELPHI}<T>{$endif}.GetValue(aArg: T): T;
  15. begin
  16.   Result := aArg;
  17. end;
  18.  
  19. var
  20.   a: LongInt;
  21. begin
  22.   a := {$ifndef USE_MODE_DELPHI}specialize{$endif} TTest<LongInt>.GetValue(42) +
  23.          {$ifndef USE_MODE_DELPHI}specialize{$endif} TTest<LongInt>.GetValue(21);
  24. end.

This will lead to similar errors as in the original example when compiled with USE_MODE_DELPHI defined.

  • Is there or will there be a compiler option to use the Delphi mode generic with FPC mode without leaving the FPC mode?

There is not, but I have it on my todo list to introduce such a modeswitch. But this is very low priority and needs some other things working first. Please note that the same restrictions as in mode Delphi would apply then, cause it's not the mode per-se that's the cause for these restrictions, but the presence or absence of the specialize keyword which makes things easier for the parser.

  • Can I ask what is your idea about what is the better option in the long run? The Delphi generic mode or the FPC one?

I personally favor the non-Delphi mode generics.

Okoba

  • Hero Member
  • *****
  • Posts: 528
Re: Generic record enumerator in Delphi mode
« Reply #11 on: June 20, 2021, 11:14:26 pm »
Thank you for the clarification.
I will continue to work with generics and report back.

 

TinyPortal © 2005-2018