[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