Recent

Author Topic: Generics in Freepascal  (Read 12176 times)

kapibara

  • Hero Member
  • *****
  • Posts: 610
Generics in Freepascal
« on: September 04, 2015, 12:27:51 am »
I just dont get it. How to convert this basic example for Delphi to FreePascal?

Code: [Select]
type
  TField<T> = class
  private
    FValue: T;
  protected
    function GetValue: T; virtual;
    procedure SetValue(const AValue: T); virtual;
  public
    property Value: T read GetValue write SetValue;
  end;

  TTrimmingStringField = class(TField<string>);
  protected
    function SetValue(const AValue: string); override;
  end;

  // Now use these fields as you'd expect

implementation

{ TField<T> }

function TField<T>.GetValue: T;
begin
  Result := FValue;
end;

procedure TField<T>.SetValue(const AValue: T);
begin
  FValue := AValue;
end;

{ TTrimmingStringField }

procedure TTrimmingStringField.SetValue(const AValue: string);
begin
  inherited SetValue(Trim(AValue));
end;

Source: http://stackoverflow.com/questions/32340445/polymorphism-and-properties/
« Last Edit: September 06, 2015, 10:59:50 pm by kapibara »
Lazarus trunk / fpc 3.2.2 / Kubuntu 22.04 - 64 bit

Jurassic Pork

  • Hero Member
  • *****
  • Posts: 1228
Re: Generics in Freepascal
« Reply #1 on: September 04, 2015, 02:41:11 am »
hello,
Generics are available both in ObjFPC and Delphi modes and generic records/arrays/procedural variables are defined similar to generic classes. Support for Delphi's generic syntax has been added but still may be not 100% compatible (FPC New Features 2.6.0).
use  {$MODE DELPHI} or  generic and specialize reserved words for objfpc like that :
Code: [Select]
type
  generic TField<T> = class
  private
    FValue: T;
  protected
    function GetValue: T; virtual;
    procedure SetValue(const AValue: T); virtual;
  public
    property Value: T read GetValue write SetValue;
  end;

  TDateTimeField = specialize TField<TDateTime>;
  TStringField = specialize  TField<string>;

//  TTrimmingStringField = class(specialize TField<string>);
TTrimmingStringField = class(TStringField)
  protected
    procedure SetValue(const AValue: string); override;
end;

implementation
{ TField<T> }

function  TField.GetValue: T;
begin
  Result := FValue;
end;

procedure  TField.SetValue(const AValue: T);
begin
  FValue := AValue;
end;

{ TTrimmingStringField }

procedure TTrimmingStringField.SetValue(const AValue: string);
begin
  inherited SetValue(Trim(AValue));
end;

Friendly J.P
« Last Edit: September 04, 2015, 02:47:50 am by Jurassic Pork »
Jurassic computer : Sinclair ZX81 - Zilog Z80A à 3,25 MHz - RAM 1 Ko - ROM 8 Ko

Leledumbo

  • Hero Member
  • *****
  • Posts: 8757
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Generics in Freepascal
« Reply #2 on: September 04, 2015, 08:55:00 am »
Yep, {$mode delphi} compiles your code fine.

kapibara

  • Hero Member
  • *****
  • Posts: 610
Re: Generics in Freepascal
« Reply #3 on: September 05, 2015, 04:43:36 am »
Great, that works.

Do you also know if its possible to somehow make this work with polymorphism? For example, can a TStringField and a TIntegerField somehow be assigned to a base class variable like TField ?

I want to put instances of different TFields descendents (TStringField and TIntegerField) in the same list, maybe make it typesafe, and access them as usual with polymorphism.

Using the base class didn't work out:

Code: [Select]
aField: TField;
Error: Generics without specialization cannot be used as a type for a variable
Lazarus trunk / fpc 3.2.2 / Kubuntu 22.04 - 64 bit

derek.john.evans

  • Guest
Re: Generics in Freepascal
« Reply #4 on: September 05, 2015, 09:55:52 am »
You can, but the code gets very messy. See below for an example. Maybe what you are looking for is a typesafe variant? Which provides the same functionality as a polymorphic implementation.

This solution has the advantage of being small, while providing a type safe read/write, and a variant read. Example:
Code: Pascal  [Select][+][-]
  1.   generic TTypeSafeVariant<T> = class
  2.   strict private
  3.     FValue: variant;
  4.   protected
  5.     function GetValue: T; virtual;
  6.     procedure SetValue(const AValue: T); virtual;
  7.   public
  8.     constructor Create;
  9.   public
  10.     property Value: T read GetValue write SetValue;
  11.     property AsVariant: variant read FValue;
  12.   end;
  13.  
  14.   constructor TTypeSafeVariant.Create;
  15.   begin
  16.     inherited;
  17.     FValue := Default(T);
  18.   end;
  19.  
  20.   function TTypeSafeVariant.GetValue: T;
  21.   begin
  22.     Result := FValue;
  23.   end;
  24.  
  25.   procedure TTypeSafeVariant.SetValue(const AValue: T);
  26.   begin
  27.     FValue := AValue;
  28.   end;  
  29.  

And this is a small part of a polymorphic solution. It gets very messy, but, you may like it:
Code: Pascal  [Select][+][-]
  1. type
  2.  
  3.   TFieldAbstract = class
  4.   protected
  5.     function AsInteger: Integer; virtual;
  6.     function AsString: String; virtual;
  7.     function AsDateTime: TDateTime; virtual;
  8.     function AsSingle: Single; virtual;
  9.     function AsVariant: variant; virtual; abstract;
  10.   end;
  11.  
  12.   function TFieldAbstract.AsString: String;
  13.   begin
  14.     Result := AsVariant;
  15.   end;
  16.  
  17.   function TFieldAbstract.AsInteger: Integer;
  18.   begin
  19.     Result := AsVariant;
  20.   end;
  21.  
  22.   function TFieldAbstract.AsSingle: Single;
  23.   begin
  24.     Result := AsVariant;
  25.   end;
  26.  
  27.   function TFieldAbstract.AsDateTime: TDateTime;
  28.   begin
  29.     Result := AsVariant;
  30.   end;
  31.  
  32. type
  33.  
  34.   generic TField<T> = class(TFieldAbstract)
  35.   strict private
  36.     FValue: T;
  37.   protected
  38.     procedure SetValue(const AValue: T); virtual;
  39.     function AsVariant: variant; override;
  40.   public
  41.     property Value: T read FValue write SetValue;
  42.   end;
  43.  
  44.   procedure TField.SetValue(const AValue: T);
  45.   begin
  46.     FValue := AValue;
  47.   end;
  48.  
  49.   function TField.AsVariant: variant;
  50.   begin
  51.     Result := FValue;
  52.   end;
  53.  
  54. type
  55.  
  56.   TFieldDateTime = class(specialize TField<TDateTime>)
  57.   protected
  58.     function AsDateTime: TDateTime; override;
  59.   end;
  60.  
  61.   function TFieldDateTime.AsDateTime: TDateTime;
  62.   begin
  63.     Result := Value;
  64.   end;
  65.  
  66. type
  67.  
  68.   TFieldString = class(specialize TField<String>)
  69.     function AsString: String; override;
  70.     function AsInteger: Integer; override;
  71.   end;
  72.  
  73.   function TFieldString.AsString: String;
  74.   begin
  75.     Result := Value;
  76.   end;
  77.  
  78.   function TFieldString.AsInteger: Integer;
  79.   begin
  80.     Result := StrToInt(Value);
  81.   end;
  82.  
  83. type
  84.  
  85.   TFieldInteger = class(specialize TField<Integer>)
  86.     function AsInteger: Integer; override;
  87.   end;
  88.  
  89.   function TFieldInteger.AsInteger: Integer;
  90.   begin
  91.     Result := Value;
  92.   end;
  93.  
  94. type
  95.  
  96.   TFieldSingle = class(specialize TField<Single>)
  97.     function AsSingle: Single; override;
  98.   end;
  99.  
  100.   function TFieldSingle.AsSingle: Single;
  101.   begin
  102.     Result := Value;
  103.   end;  
  104.  

NOTE: Neither pieces of code have been fully tested.
« Last Edit: October 02, 2015, 03:12:40 am by Geepster »

kapibara

  • Hero Member
  • *****
  • Posts: 610
Re: Generics in Freepascal
« Reply #5 on: September 06, 2015, 08:05:27 pm »
I actually tried with variants first, before I asked the question. But this will be used from inside PascalScript and I didn't find out how to get a TDateTime in and out of a Variant from *inside* the script. So I go for your second example and hopefully sidestep that issue. At least for now.  ::)

Your code compiled, and since the property "Value" exists in the TField class I declared a TField variable:

Code: [Select]
aField: TField;
Error: Generics without specialization cannot be used as a type for a variable

Then I tried this:

Code: [Select]
aField: TFieldAbstract;
aField:= TFieldString.Create;
aField.Value:= 'Hello';

Error: identifier idents no member "Value"

I have more to learn about fpc generics..
« Last Edit: September 06, 2015, 08:12:36 pm by kapibara »
Lazarus trunk / fpc 3.2.2 / Kubuntu 22.04 - 64 bit

derek.john.evans

  • Guest
Re: Generics in Freepascal
« Reply #6 on: September 06, 2015, 08:22:50 pm »
Yes, the TFieldAbstract version is correct. RE: You code:
Code: [Select]
aField.Value:= 'Hello';

For this to work with a base class, Value has to accept any basic variable type.

It all seems like a catch 22. You want a type safe class for variables, but then you want a non-typesafe basic class for assignments.

Eventually, you get back to using variants.

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Generics in Freepascal
« Reply #7 on: September 06, 2015, 08:29:16 pm »
I never understand it that why FreePascal generic system is different than Delphi's and Delphi's seems cleaner and easier.
Can any one explain it?

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Generics in Freepascal
« Reply #8 on: September 06, 2015, 08:58:16 pm »
I never understand it that why FreePascal generic system is different than Delphi's

Quite simply, FreePascal (initial) generics were historically developed and implemented before Delphi's. Delphi developers did not then later care about compatibility with the existing FreePascal syntax. Caring about compatibility seems to work in only one direction.

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Generics in Freepascal
« Reply #9 on: September 06, 2015, 09:37:08 pm »
I didn know that.So why we dont use Delphi's structure? I used it in my FPC programs but many times I dont want to use Delphi mode. As I mentioned before this changing mode is a good choice but confusing because some  good feature are in Delphi mode like this and advanced record (yse I know it has its own define but I dont like to add its definition every time I want an advance record,I like Pascal because its easy to use feature) and some features are in objFPC mode and generics are one of my problems that I have from day one of using Lazarus.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Generics in Freepascal
« Reply #10 on: September 06, 2015, 10:43:08 pm »
So why we dont use Delphi's structure?
I'm not sure who "we" represents in your question.
FreePascal has extended its syntax (complicating the compiler) to accommodate (most of) the later-appearing Delphi generics syntax if you prefer to use it. Activated by a {$mode delphi}, which seems logical enough. How could it be more compatible?

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Generics in Freepascal
« Reply #11 on: September 06, 2015, 10:56:07 pm »
So why we dont use Delphi's structure?
I'm not sure who "we" represents in your question.
We represent Lazarus users with default choices.
Quote
How could it be more compatible?
By changing the default to best system.
And it very easy to understand that I dont like to use Delphi mode in a Lazarus program!
Yes I can use when Im converting a Delphi program but not when Im making the project from scratch in Lazarus.
that comes to my my first question, with is the best? Delphi's one seems easier and cleaner code , where FPC generics goes better?
For now I use Delphi mode or using another way but I want to know more about this subject.

Thaddy

  • Hero Member
  • *****
  • Posts: 14359
  • Sensorship about opinions does not belong here.
Re: Generics in Freepascal
« Reply #12 on: September 07, 2015, 12:02:44 pm »
I'm not sure who "we" represents in your question.
[/quote]
We represent Lazarus users with default choices.
Quote
How could it be more compatible?
By changing the default to best system.
And it very easy to understand that I dont like to use Delphi mode in a Lazarus program!
Yes I can use when Im converting a Delphi program but not when Im making the project from scratch in Lazarus.
that comes to my my first question, with is the best? Delphi's one seems easier and cleaner code , where FPC generics goes better?
For now I use Delphi mode or using another way but I want to know more about this subject.
[/quote]

The "we" is really not in place if you mean "we" as a community. It is probably the other way around for people who come from Delphi.
That said: mode objfpc is not legacy, it has some distinct merits, like being more picky about constructs. But it takes getting used to. It is definitely not your average flavor. But "we" is not "us".
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Generics in Freepascal
« Reply #13 on: September 07, 2015, 12:43:30 pm »
OK sorry for saying we but my question stands,What is the best choice and if it is Delphi's way (that it seems to me) why this is not default way and if not what feather FPC generics have more than others?

Leledumbo

  • Hero Member
  • *****
  • Posts: 8757
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Generics in Freepascal
« Reply #14 on: September 07, 2015, 02:10:49 pm »
What is the best choice
Describe "best". I PREFER ObjFPC style because it looks better to my eyes. Delphi generics remind me of ugly C++/Java style that requires you to type <xxx> over and over again. ObjFPC nowadays have similar semantics, but at least from syntax point of view it enforces type definition first.
why this is not default way and if not what feather FPC generics have more than others?
It will break existing code relying on it.

 

TinyPortal © 2005-2018