Recent

Author Topic: Adventures in Generics  (Read 7479 times)

carl_caulkett

  • Hero Member
  • *****
  • Posts: 654
Adventures in Generics
« on: February 17, 2016, 03:06:41 pm »
Hello, I'm just starting to investigate generics, and in time old tradition, I've built a numeric vector class which is designed to implement the usual list type operations on various numeric types. So, my base class is TcaVector<T> to which I have added things like TcaDoubleVector = specialize TcaVector<Double> and TcaIntegerVector = specialize TcaVector<Integer>. So far so good. Now I have a situation where I would like use virtual methods to bring in some polymorphism into the proceedings. The problem is what class to use as the common ancestor? Just using TcaVector does not compile and my attempts to use TcaVector<T> started giving some quite bizarre compiler errors.

Hope this question makes sense! Over to you, my learned friends...

Cheers,
Carl
"It builds... ship it!"

sky_khan

  • Guest
Re: Adventures in Generics
« Reply #1 on: February 17, 2016, 05:33:35 pm »
All generic container classes like TFPGList, TFPGObjectList... in fgl unit ( {lazarus}/fpc/{version}/source/rtl/objpas/fgl.pp ) inherits from TFPSList. Have you look at that unit ?

carl_caulkett

  • Hero Member
  • *****
  • Posts: 654
Re: Adventures in Generics
« Reply #2 on: February 17, 2016, 05:36:39 pm »
Yeah, I'd forgotten about those classes. I'll check them out again and see if I can adopt a similar approach. Thanks for the suggestion.
"It builds... ship it!"

Leledumbo

  • Hero Member
  • *****
  • Posts: 8836
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Adventures in Generics
« Reply #3 on: February 17, 2016, 06:19:29 pm »
This is how you define it in ObjFPC generics style:
Code: Delphi  [Select][+][-]
  1. type
  2.   generic TBaseClass<T> = class
  3.     procedure whatever; virtual;
  4.   end;
  5.  
  6.   generic TDerivedClass<T> = class(specialize TBaseClass<T>)
  7.     procedure whatever; override;
  8.   end;
  9.  
  10.   // or
  11.  
  12.   TAnotherDerivedClass = class(specialize TBaseClass<Double>)
  13.     procedure whatever; override;
  14.   end;
  15.  

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4724
  • I like bugs.
Re: Adventures in Generics
« Reply #4 on: February 17, 2016, 07:01:29 pm »
This is how you define it in ObjFPC generics style:
[...]

That looks very hackinsh to me. I would use just generics container classes as they are and do polymorphism with other classes which can own those containers.
Generics don't have the same inheritance rules than other classes have. It is a language feature, classes in fgl unit are no different in this respect.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

Leledumbo

  • Hero Member
  • *****
  • Posts: 8836
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Adventures in Generics
« Reply #5 on: February 17, 2016, 08:03:50 pm »
That looks very hackinsh to me.
Not at all to me. Looks logical whether you want to create a generic class extending another generic class, specializing with the same generic parameter or simply extending a specialized generic class.

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Adventures in Generics
« Reply #6 on: February 17, 2016, 10:15:51 pm »
This is how you define it in ObjFPC generics style:
[...]

That looks very hackinsh to me. I would use just generics container classes as they are and do polymorphism with other classes which can own those containers.
Generics don't have the same inheritance rules than other classes have. It is a language feature, classes in fgl unit are no different in this respect.
Juha can you make an example?
I couldn't use generics in Lazarus as simply as in Delphi.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12984
  • FPC developer.
Re: Adventures in Generics
« Reply #7 on: February 18, 2016, 10:38:46 am »
I couldn't use generics in Lazarus as simply as in Delphi.

You need to download generics.collections from the freesparta git repo, but then my Delphi code worked quite fine with 3.0. All my code is in $mode delphi though.

Thaddy

  • Hero Member
  • *****
  • Posts: 19624
  • Glad to be alive.
Re: Adventures in Generics
« Reply #8 on: February 18, 2016, 12:10:27 pm »
You need to download generics.collections from the freesparta git repo, but then my Delphi code worked quite fine with 3.0. All my code is in $mode delphi though.

Download from here: https://github.com/dathox/generics.collections

It also has examples in the repo.
Any "programmer" that knows only one programming language is not a programmer

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4724
  • I like bugs.
Re: Adventures in Generics
« Reply #9 on: February 18, 2016, 12:34:22 pm »
Juha can you make an example?

Yes, I guess the inheritance syntax is OK. A specialized type of a generics class does not inherit from the generics class, but defining a new "class" from it brings back the inheritance. I am also trying to wrap my mind around this topic.

The original question was about a container class, TcaVector. Extending it with polymorphism may potentially be bad design but it is not related to generics. It applies to any container.
  https://en.wikipedia.org/wiki/Composition_over_inheritance
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Adventures in Generics
« Reply #10 on: February 18, 2016, 12:55:37 pm »
I couldn't use generics in Lazarus as simply as in Delphi.

You need to download generics.collections from the freesparta git repo, but then my Delphi code worked quite fine with 3.0. All my code is in $mode delphi though.
Still need to use Delphi mode?
I really dont like to use delphi mode, Im in Lazarus and using FPC so I dont like to use it and it make comparability issue sometimes when I use units with Delphi mode from units with objfpc mode.
Thanks Thaddy and Juha.

carl_caulkett

  • Hero Member
  • *****
  • Posts: 654
Re: Adventures in Generics
« Reply #11 on: February 18, 2016, 11:55:43 pm »
Juha, I take your point about composition over inheritance, although in this case it's a small layer of virtual methods intended to address the fact that, as far as I know,
there is no method that will convert any numeric. integer or floating, to a string, a NumToStr function if you will.

Anyhow, I solved my immediate problem by introducing a TcaVectorBase class from which the TcaVector<T> class descends followed by the various class specific to the
various numeric types. I did run into one other issue. In an attempt to use the Observer pattern in order to get changes to my vector class triggered, I ended up descending
my TcaVectorBase class from my own copy of TFPSList altered to descend from TInterfacedObject, so that I could use an interface based IcaSubject using "implements".

Of course, I am aware that I don't need to go to all this trouble, but this is hobby stuff for me, at the moment so it's an opportunity to experiment and see what's possible.

Cheers,
Carl
"It builds... ship it!"

carl_caulkett

  • Hero Member
  • *****
  • Posts: 654
Re: Adventures in Generics
« Reply #12 on: February 19, 2016, 03:19:25 am »
>>>  a NumToStr function if you will.

Of course I could always do it in two lines. One to assign the number to a float, and the second to do a FloatToStr.
Duh!!
The dangers of overthinking a problem. Anyhow, much refactoring later, I've gotten rid of the use of the TFPSList copy  (I felt dirty using that to be honest!) and I've got what I think is a quite nice Assign method...

Code: Pascal  [Select][+][-]
  1. procedure TcaVectorBase.Assign(Source: TcaVectorBase);
  2. var
  3.   Index: Integer;
  4.   P: Pointer;
  5. begin
  6.   Clear;
  7.   for Index := 0 to Pred(Source.Count) do
  8.     begin
  9.       GetMem(P, GetElementSize);
  10.       Move(Source.Pointers[Index]^, P, GetElementSize);
  11.       FList.Add(P);
  12.     end;
  13. end;
  14.  

The GetElementSize method is virtual and abstract and is handled in the generic class...

Code: Pascal  [Select][+][-]
  1. function TcaVector.GetElementSize: Integer;
  2. begin
  3.   Result := SizeOf(T);
  4. end;
  5.  

It's only 2.20 in the morning. Just one more compile...

Cheers,
Carl
"It builds... ship it!"

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12984
  • FPC developer.
Re: Adventures in Generics
« Reply #13 on: February 19, 2016, 10:48:27 am »
Still need to use Delphi mode?

If I have a choice I always use Delphi mode. Never had much problems with modes between units.

aradeonas

  • Hero Member
  • *****
  • Posts: 824
Re: Adventures in Generics
« Reply #14 on: February 19, 2016, 01:02:43 pm »
Still need to use Delphi mode?

If I have a choice I always use Delphi mode. Never had much problems with modes between units.
It feels weird for best solution use Delphi mode.

 

TinyPortal © 2005-2018