[fpc-devel] Generics Basics

rstar at mnet-online.de rstar at mnet-online.de
Wed Nov 9 00:35:28 CET 2005


dannym wrote:

>Hi,
>
>Am Dienstag, den 08.11.2005, 18:10 -0200 schrieb Felipe Monteiro de
>Carvalho:
>  
>
>>Hello,
>>
>>I am trying to understand what exactly generics are. I read the wiki
>>page, but there are lot's of code examples and very few explanations.
>>Can someone explain it to me in a (relatively) simple way?
>>
>>What problem is it trying to solve?
>>    
>>
>
>It makes types parametrizable.
>
>For example, if you write a list class, traditional delphi has TList.
>However, this TList can only contain TObject, no double/integer/... . 
>Moreover, you can put objects of *differing*  (i.e. unrelated) classes
>in the same list, which is most of the time a bad bad idea.
>
>Excuse me for resorting to an example again, but it's just easiest to
>see:
>
>1) without generics
>
>type
>  TAppleList = TList;
>
>  TApple = class
>  end;
>  TOrange = class
>  end;
>
>var
>  apples: TAppleList;
>  apple: TApple;
>begin
>  apples.Add(TApple.Create); // works
>  apples.Add(TOrange.Create); // works, and is stupid
>
>  apple := apples[0]; // compile error
>  apple := apples[1]; // compile error
>  apple := apples[0] as TApple; // manual cast, works
>  apple := apples[1] as TApple; // compiles, breaks at runtime
>end;
>
>Generic types, on the other hand, define just the TList, but do not fix
>the contained type in it, but leave it as a parameter to specify later. 
>
>2) with generics
>
>type
>  TListItem = generic(T) record
>    Data: T;
>    Next: TListItem(T);
>  end;
>  PListItem = ^generic(T) TListItem(T);
>
>  TList = generic(T) class
>  private
>     fHead: PListItem(T);
>     fTail: PListItem(T);
>  published
>     procedure Add(Item: T);
>  end;
>
>procedure TList.Add(Item: T);
>var
>  node: PListItem(T);
>begin
>  New(node);
>  node^.Data := Item;
>  node^.Next := nil;
>
>  if Assigned(fTail) then begin
>    fTail^.Next := node;
>  end else begin
>    fHead := node;
>  end;
>
>  fTail := node;
>end;
>
>type
>  TApple = class
>  end;
>  TOrange = class
>  end;
>
>  TAppleList = TList(TApple);
>
>var
>  apples: TAppleList;
>  apple: TApple;
>begin
>  apples.Add(TApple.Create); // works
>  apples.Add(TOrange.Create); // compile error
>
>  apple := apples[0]; // works
>  apple := apples[1]; // not applicable
>  apple := apples[0] as TApple; // works, but unneccessary
>  apple := apples[1] as TApple; // not applicable
>end;
>
>  
>
>>And how do generics relate to interfaces?
>>    
>>
>
>interfaces in pascal are mostly runtime-bound. 
>Generics are mostly resolved at compile time. 
>Otherwise quite similar, with the exception that there is no "mother of
>everyone" class, that is, a class which is the base class of all other
>types, also of i.e. Integer.
>
>i.e.
>Integer = class(TAll, IUnknown);
>Double = class(TAll, IUnknown);
>Boolean = class(TAll, IUnknown);
>TObject = class(TAll, IUnknown);
>TFoo = class(TObject);
>
>note that I'm not advertising that there should be one, just noting the
>facts. 
>
>The fact being, if there were one, interfaces would do the same as
>generics, just at runtime. 
>Without one, interfaces do nearly the same as generics (just don't work
>for simple types), but still work only at runtime (at huge "cost"). 
>
>Doing stuff at runtime slows the program down, and also note that the
>more you do at runtime, the more stuff the compiler has to compile in at
>each place just in case something x or y happens at runtime, at every
>place.
>Worse, if the language is not really designed for stuff to be determined
>at runtime (i.e. late bound stuff), it sucks. Therefore you have to add
>"as TApple". Because the language just doesn't expect that you want it
>to automagically upcast back to what it was.
>
>If it were a language designed for stuff to be determined at runtime too
>(late bound), from the line
>
>apple := apples[0];
>
>it would automagically generate (invisible for the programmer, but in
>the executable):
>  var 
>    temp: TObject;
>  
>  temp := apples[0];
>  if apple is TApple then 
>    apple := temp as TApple
>  else
>    raise ETypeError.Create...;
>
>that is, it would add code for "runtime type inference".
>
>(Note that the "is TApple" and "as TApple" are the destination type of
>the variable "apple", i.e. the compiler still wouldn't know what type is
>_in_ the list, it just knows you _want_ to have an TApple. 
>If you instead specified orange: TOrange; and accessed: orange :=
>apples[0];, fine, it will cast to TOrange, "you asked for it, you get
>it")
>
>Which would be a little better than now, but slower. 
>
>(as a side note, note the only reason why anybody bothers with type safe
>compiled languages is strong type checking, that is total _compile time_
>strong type checking, also known as "if it compiles, it works (mostly)".
>If it weren't for that advantage (ok, and the speed advantage when done
>right), nobody would use strongly typed languages)
>
>Generics, therefore, move the complexity into the type checker of the
>compiler instead, the benefit being generation of faster code,
>compile-time (automatically) verifyable code, but at the cost of a
>larger executable size.
>
>  
>
>>thanks,
>>
>>Felipe Monteiro de Carvalho
>>    
>>
>
>I hope I didn't commit major blunders in the explaination, but it should
>be about right :)
>
>cheers,
>   Danny
>
>
>_______________________________________________
>fpc-devel maillist  -  fpc-devel at lists.freepascal.org
>http://lists.freepascal.org/mailman/listinfo/fpc-devel
>
>
>  
>
perfect!




More information about the fpc-devel mailing list