Sign in Join
Wiki copytemporary

Generics/ko

한국어 · English · Français · Polski · Русский

The first thing someone who knows thie stuff needs to do is explain what a generic is so we understand what the issue is.

FPC에서 제너릭

Generic classes are already implemented in the 2.3.1 version of FPC. 제너릭 클래스들은 이미 FPC 2.3.1 버전안에 구현되어져 있습니다.

See the FPC reference. 여기를 보세요 FPC reference.

This page contains proposals and ideas, which lead to the current implementation and for further features. 이 문서는 앞으로의 모습과 성취물을 안내하는, 제안과 아이디어를 포함하고 있습니다.

왜?

See "Already existing stuff" for possible but lacking ways right now. 당장 약간 모자라지만, 가능하면 "이미 존재하는 것"을 읽어보세요

관심을 갖은 사람들

- Almindor - dannym - fpk - giantm - oliebol - plugwash - Synopsis - tom - PublicJoke - neli - dr_evil

사용 례 (분류안됨)

TODO: combine the below stuff into one example using the <> syntax TODO: <> 문법을 사용하는 예제와 아래의 것을 결합합니다

(tom 의 제안)

pascal
 type
 // explicit addition of "generic" keyword. Maybe something like "uses generic <...>" 
 // better use the <>-brackets because otherwise it may cause confusion with arrays 
 // in the actual instantiation
 // "generic"키워드를 명시합니다. 실제 인스턴스에서 배열들과 함께 혼동을 일으킬 것이므로, 
 // <>괄호를 쓰는 것보다 "generic <...>" 을 쓰는 것이 나을것입니다
 abc = class(..) generic <a, b>
 end;
 var
       //  because they're still unused ;)
       // 왜냐면 그것들은 아직 사용되지 않았습니다 ;)
       xyz : abc<String, Integer>;
       xyz2 : abc<AObj1, AObj2>;
 begin
   // don't repeat generics in instance creation (if not necessary)
   // 인스턴스의 초기화에 제너릭을 반복하지 마십시요(필요하지 않다면)
   xyz := abc.Create;
   xyz2 := abc<descendant_of_Aobj1, descendant_of_Aobj2>.Create;
 end.

(dannym 의 새로운 제안)

pascal
 type
 GList = generic class of T
 public
   procedure Add(const stuff: T);
 end;
pascal
 procedure GList.Add(const stuff: T); // note that here no 'of T' //'of T'가 없음을 유의하세요
 begin
   ...
 end;
pascal
 var
   intlist: GList of Integer;
 begin
   intlist := GList.Create; // note that here the detail type is missing and 
           // needs to be deducted from the var declaration
           // 여기에 자세한 선언형이 없으며, var 선언으로 (선언형이)빠져야만 한다는 것을 알아두세요
pascal
   //or intlist := typeof(intlist).Create;
   //intlist := GList of Integer.Create; <-- this is unclear if that works <-- 이 것이 실제 구현된 것이라면, 명확하지 않습니다
 end;

(dannym 의 옛 제안)

pascal
 type
 GList<T> = class
 public
   procedure Add(const stuff: T);
 end;
pascal
 procedure GList<T>.Add(const stuff: T);
 begin
   ...
 end;
pascal
 var
   intlist: GList<Integer>;
 begin
   intlist := GList<Integer>.Create;
 end;

plugwash 의 것:

text
 tmycollection=template(tobject)[x]
 tintegercollection=tmycollection[integer]

oliebol 의 것:

pascal
 template blaat<t:integer>(a:t);
 var x : t;
 begin
   do something with a, and use t with it
 end;

note that we should have a generics section (like interface and implementation sections) containing the generics: 우리는 (interface와 implementation 섹션들처럼) 제너릭들을 포함하는, generics 섹션을 가져야한다는것을 알립니다

pascal
 unit xyz;
 generics
 type
   GList......
 procedure GList.Add(const value: T);
 ....
 end.

(eiffel의 것)

http://paste.lisp.org/display/6236

(PublicJoke 의 제안)

pascal
 unit Unit1(TMathType);
 
 interface
 
 type
   TMathClass = class
   public
     class function Add(a1, a2: TMathType): TMathType;
   end;
 
 implementation
 
 class function TMathClass.Add(a1, a2: TMathType): TMathType;
 begin
   Result:=a1+a2;
 end;
 
 end.
pascal
 unit Unit2;
 
 type
   TMathType = Longword;
 
 interface
 
 implementation
 
 end.
pascal
 program prog;
 
 uses
   Unit2, 
   Unit1(Byte) as ByteMath in 'Unit1.pas',
   Unit1(TMathType) as PolyMath in 'Unit1.pas';
 
 type
   TByteMathClass = ByteMath.TMathClass;
   TPolyMathClass = PolyMath.TMathClass;
 
 var
   b: Byte;
   p: TMathType;
 
 begin
   b:=TByteMathClass.Add(1, 2);
   p:=TPolyMathClass.Add(3, 4);
 end.

(Gadnio 의 제안) I personally think the C++ way of doing this is clear enough AND will be the easiest to parse/understand: 저는 개인적으로 C++의 방법이 이것을 충분히 명확하게 하고, 해석과 이해가 쉬워질꺼라고 생각합니다

pascal
 template <T>
   TGenericObject< T > = record
     Data : T;
     OtherStuff : Pointer;
   end;{TGenericObject}
 template <TKey, TData = TGenericObject< boolean > >
   TGenericList = class( TWhee< TKey > )
   public
     function Lookup( const Key : TKey ): TData;
     template <THihi>
       class function DataToHihi( Data : TData ): THihi;
   end;{TGenericList}

Also, note that template specialization is a very, very important thing (for me especially). In C++ they've got vector< bool > that behaves different than the standard vector< T >. An example: 또한, template의 특수함은 매우, 매우 중요한 것임을 알아야 합니다(특히 나에게는). C++에서는 표준 vector< T >와는 다른 동작을 하는 vector< bool >을 가지고 있습니다. 한 예로..

pascal
 template <T, TT>
   TSame = class
     class function Same: Boolean;
   end;{TSame}
 template <T>
   TSame = class
     class function Same: Boolean;
   end;{TSame}
pascal
 template <T, TT>
 class function TSame.Same: Boolean;
 begin
   Result := false;
 end;{TSame<T, TT>.Same}
 template <T>
 class function TSame.Same: Boolean;
 begin
   Result := true;
 end;{TSame<T>.Same}

This allows to make a compile-time check for same types that are not TObject, e.g. 이 것은 TObject가 아닌, 같은 선언형에 대해서 compile-time 검사를 할수 있게 합니다. 예로 들면..

pascal
 TFoo = class;
 TBar = class( TFoo );
 var
   S : Boolean;
 begin
   S := TSame< String, Integer >.Same; //s = false
   S := TSame< Int64, Integer >.Same; //s = false
   S := TSame< String, String >.Same; //s = true
   S := TSame< TBar, TFoo >.Same;//s = false (not the same as "var x: TFoo; begin x := TBar.Create; end;" )
                                             // "var x: TFoo; begin x := TBar.Create; end;" 가 같지 않듯이)
 end;

Another thing to note: It would be very good if something like this (c++ syntax) would be implemented: 다른 한가지를 적자면, 만약 이 런것이(C++ 문법) 시행되어진다면, 매우 좋아질 겁니다

pascal
 template <bool b>
 class h
 {
   public:
   static const int i = 0;
 };//h
pascal
 template <>
 class h< true >
 {
   public:
   static const int i = 1;
 };
pascal
 template <>
 class h< false >
 {
   public:
   static const int i = 2;
 };

Here 여기는 항상

text
 h<false>.i = 2
 h<true>.i = 1

ALWAYS. 이렇게 됩니다

사용 례

If you add more syntax examples, please add equivalent to both. 만약 좀더 문법예제를 추가한다면, 두가지에 상응하는 것을 추가해 주세요. Two main syntaxes seem to be popular, "<>" which looks more or less like C++, c#, etc.. and adding a "generic" keyword which is more along the Pascal style of code. Go to Generics Vote to vote for one or the other syntax. 두 가지 기본 문법들은 인기있어질 것으로 보이는데, 어느 정도 C++, C# 등의 것으로 보이는 "<>"과 코드의 방식이 좀더 Pascal스러운, 추가된 "generic" 낱말입니다.

괄호 사용하기: <>

pascal
 type
   TGenericCollection<T: TCollectionItem> = class(TComponent)
   ...implement TCollection and use T
   end;
text
   TCollection = TGenericCollection<TCollectionItem>;
   TFieldDefs = TGenericCollection<TFieldDef>;
pascal
   TGenericList<T: PtrInt> = class(TObject)
   ...implement TList and use PtrInt size for code generation
   end;
text
   TList = TGenericList<Pointer>;

Implementation of TGenericCollection can be compiled as if (T: TCollectionItem) were used. TGenericCollection의 구현은 마치 (T: TCollectionItem)이 사용된 것처럼 컴파일 되어질 것입니다

Would this solve the circular dependency ? It seems so to me, because one knows at least as much as in current implementation of these classes, but I'm no compiler engineer. 여기서 순환참조를 해결할 수 있을까요? 저는 컴파일러 엔지니어가 아니지만, 적어도 하나가 현재 이 클래스들의 구현만큼 알기 때문에 가능하다고 봅니다

For procedures: procedure 로써:

pascal
 function MyFunc<T: TTreeItem>(A: T): T;
 begin
  // do something with the tree item
  // tree 아이템과 함께 무언가를 합니다
 end;
pascal
 function Max<T>(A, B: T): T;
 begin
   if A < B then
     Result := B
   else
     Result := A;
 end;

My restrictions won't allow implementing generic Max and Min, I guess. That really needs macro-alike stuff (for the compiler implementation). 제가 생각하기로, 저의 제한은 Max, Min제너릭 구현을 허용할것 같지 않습니다. 그 것은 매크로 비슷한 것이 정말 필요해집니다(컴파일러 구현으로서)

Another example for linked lists: (ripped from mail to fpc-devel of dannym, but modified a bit) 링크드 리스트로 다른 예제가 있습니다(메일로 온 dannym의 fpc-devel에서 가져왔지만, 조금만 고쳤습니다)

pascal
 type
   TListItem<T> = record
     Data: T;
     Next: TListItem;
   end;
   PListItem = ^TListItem;

   TList<T> = class
   private
     fHead: PListItem<T>;
     fTail: PListItem<T>;
   published
     procedure Add(Item: T);
   end;

 procedure TList<T>.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;

"generic" 낱말을 사용하기

pascal
 type
   TGenericCollection = generic(T: TCollectionItem) class(TComponent)
   ...implement TCollection and use T
   end;
text
   TCollection = specialize TGenericCollection(TCollectionItem);
   TFieldDefs = specialize TGenericCollection(TFieldDef);
pascal
   TGenericList = generic(T: PtrInt) class(TObject)
   ...implement TList and use PtrInt size for code generation
   end;
text
   TList = specialize TGenericList(Pointer);

procedure 로써:

pascal
 function generic(T: TTreeItem) MyFunc(A: T): T;
 begin
  // do something with the tree item
  // tree 아이템 과 함께 무언가를 합니다
 end;
pascal
 function generic(T) Max(A, B: T): T;
 begin
   if A < B then
     Result := B
   else
     Result := A;
 end;

Another example for linked lists: (ripped from mail to fpc-devel of dannym, but modified a bit) 링크드 리스트로 다른 예제가 있습니다(메일로 온 dannym의 fpc-devel에서 가져왔지만, 조금만 고쳤습니다)

pascal
 type
   TListItem = generic(T) record
     Data: T;
     Next: TListItem;
   end;
   PListItem = ^TListItem;

   TList = generic(T) class
   private
     fHead: specialize PListItem(T);
     fTail: specialize PListItem(T);
   published
     procedure Add(Item: T);
   end;

 procedure generic(T) TList.Add(Item: T);
 var
   node: specialize 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 = specialize 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;

Ugly things in need to be resolved: 해결이 필요한, 안좋은 점들:

pascal
 { a generic type "TPointer" which is a pointer to type instantiated with }
 { 포인터로 생성된 "TPointer" 제너릭 선언 }
 type TPointer = generic(Type) ^Type;
 { a generic type that instantiates all the enums for Type }
 { 선언형의 모든 열거를 생성하는 제너릭 선언 }
 type TAllEnums = generic(Type)(low(Type),high(Type));

So, before a keyword can be real "nice" the following things need addressing: 그래서, keyword가 진짜 "멋져"지는것 보다 먼저, 아래의 목록들이 주소를 필요합니다

  1. Double brackets ()(), looks C-ish again
  2. 다시 C같이 보여지는 두 개의 괄호 () ()
  3. In math, one usually defines functions with the parameters on the left of the equal sign, not on the right
  4. 수학에서, 보통 equal 기호의 왼쪽에(오른 쪽이 아닌) 인자들과 함께 함수들을 선언합니다.

제안 2

Inspired by idea from oliebol in combination with ugly examples, in list of const, var, type sections, add "template" section: 안좋은 예제들의 결합 속에서 oliebol의 생각으로부터 영감을 얻어, const와 var, type 섹션들의 목록안에 "template" 섹션을 추가합니다

pascal
 const
   ThisIsAConst = 1;
 
 type
   TMyEnum = (ab, ac);
 
 template(T: TCollectionItem)
   TGenericCollection = class(TComponent)
   ...implement TCollection and use T
   end;
 
 type
   TCollection = specialize TGenericCollection(TCollectionItem);
   TFieldDefs = specialize TGenericCollection(TFieldDef); 
 
 template(T: PtrInt)
   TGenericList = class(TObject)
   ...implement TList and use PtrInt size for code generation
   end;
 
 type
   TList = specialize TGenericList(Pointer);

For procedures: procedure 로써:

pascal
 function generic(T: TTreeItem) MyFunc(A: T): T;
 begin
  // do something with the tree item
  // tree 아이템과 함께 무언가를 합니다
 end;
pascal
 function generic(T) Max(A, B: T): T;
 begin
   if A < B then
     Result := B
   else
     Result := A;
 end;

Another example for linked lists: (ripped from mail to fpc-devel of dannym, but modified a bit) 링크드 리스트로 다른 예제가 있습니다(메일로 온 dannym의 fpc-devel에서 가져왔지만, 조금만 고쳤습니다)

pascal
 template(T)
   TListItem = record
     Data: T;
     Next: TListItem;
   end;
   PListItem = ^specialize TListItem(T);

   TList = class
   type
     PItem = specialize PListItem(T);
   private
     fHead: PItem;
     fTail: PItem;
   published
     procedure Add(Item: T);
   end;

 ...implementation...
 
 procedure generic(T) TList.Add(Item: T);
 var
   node: PItem;
 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 = specialize 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;

제안 3

text
 { Chu Jetcheng, 2008-07-24 }
pascal
 program Generics;
pascal
 type
   { The compiler should maintain a global table storing each specialized type for each generic.  The 
     `of' operator is used to create a specialization for a specified generic type.  Specialized types 
     of the same `type layout' (no other words) point to the same entry in the table of specializations
     of the specialized generic type, and thus are compatible.  Generic objects and records should also 
     be allowed. }
   { 컴파일러는 각 제너릭들을 위해 특별화된 선언형을 저장하는 전역 테이블을 유지해야만 합니다. `of' 연산자는 열거된 제너릭 선언들을 위해
     특별하게 생성하려 사용되어졌습니다. 같은 `type layout'(다른 단어)의 특별화된 선언형들이 특별화된 제너릭 선언형의 테이블 안에 같은
     entry를 가리키므로, 호환될 수 있습니다. 또한 제너릭 객체들과 레코드들은 허용해야 합니다}
pascal
   TGeneric = generic class(TAncestorClass) of T1, T2
   private
     FData1: T1;
     FData2: T2;
   public
     property Data1: T1 read FData1 write FData1;
     property Data2: T2 read FData2 write FData2;
     constructor Create(const A: T1; const B: T2);
     procedure WriteData; virtual;
   end;
pascal
   TGenericDescendent = generic class(TGeneric of Integer, T3) of T3
   private
     FData3: T3;
     FObjectOfSameType: TGenericDescendent of T3;
   public
     property Data3: T3 read FData3 write FData3;
     property ObjectOfSameType: TGenericDescendent of T3 read FObjectOfSameType write FObjectOfSameType;
     constructor Create(const C: T3);
     procedure WriteData; override;
   end;
text
   TGenericIR = TGeneric of Integer, Real;
   TGenericIRClass = class of TGenericIR;
text
   TGenericISClass = class of (TGeneric of Integer, string);
pascal
   PGenericObj = generic ^TGenericObj; 
   TGenericObj = generic object of T 
     MyField: T;
     procedure WriteData;
   end;
text
 { TGeneric }
text
 { Use the same syntax for method implementations as with immediate classes. }
 { 직접 클래스들과 함께로써 method 구현들을 위해 똑같은 문법을 사용합니다 }
pascal
 constructor TGeneric.Create(const A: T1; const B: T2);
 begin
   FData1 := A;
   FData2 := B;
 end;
pascal
 procedure TGeneric.WriteData;
 begin
   Writeln(FData1);
   Writeln(FData2);
 end;
text
 { TGenericDescendent }
pascal
 constructor TGenericDescendent.Create(const C: T3);
 begin
   inherited Create(16, C);
   FData3 := C;
 end;
pascal
 procedure TGenericDescendent.WriteData;
 begin
   inherited;
   Writeln(FData3);
   if Assigned(FObjectOfSameType) then FObjectOfSameType.WriteData;
 end;
text
 { TGenericObj }
pascal
 procedure TGenericObj.WriteData;
 begin
   Writeln(MyField);
 end;
pascal
 var 
   Obj1: TGeneric of Integer, Real;
   Obj2, Obj3: TGenericDescendent of string;
   OldStyleObj: TGenericObj of Integer;
   OldStyleObjPtr: PGenericObj of Integer;
pascal
 begin
   Obj1 := TGeneric.Create(32, 3.14);
   Obj1.WriteData;
   Writeln(#9, Obj1 is TGenericIR);
   Writeln(#9, Obj1 is TGenericIS);
pascal
   Obj2 := TGenericDescendent.Create('Generic test.');
   Obj2.WriteData;
   Writeln(#9, Obj2 is TGenericIR);
   Writeln(#9, Obj2 is TGenericIS);
pascal
   Obj3 := TGenericDescendent.Create('Another generic test.');
   Obj3.ObjectOfSameType := Obj2;
   Obj3.WriteData;
text
   Obj3.Free;
   Obj2.Free;
   Obj1.Free;
pascal
   OldStyleObj.MyField := 100;
   OldStyleObj.WriteData;
pascal
   OldStyleObjPtr := New(PGenericObj of Integer);
   with OldStyleObjPtr^ do begin
     MyField := 202;
     WriteData;
   end;
   Dispose(ObjStyleObjPtr);
 end.

Terms

type user:

immediate type:

generic type:

specialization:

type parameters:

specialized type:

변화들

class/record선언 표현 의 변화들

Each class definition representation has added fields for:

pascal
 - class instantiation mode
    0  immediate type
    1  generic type, no instantiation, just generate specialized type
    2  specialized type
pascal
 - when mode 2:
     generic type uplink (XXXX, what, unitname, typename?)
   when mode 1:
     list of specialized types known so far (and where),
     XXXX list items
   when mode 0: 
     nothing

Changes in 'type user' compilation (for class/record types only)

Each class type user will have to check mode of the class type.

For the used class type, If mode is immediate

text
 - proceed as always till now.

If mode is specialized

pascal
 - proceed as always till now 
 - keep in mind the generic type for some checks later (and debugging).

If mode is generic, this:

pascal
 - check 'list of specialized types' for the type parameter to use.
   If there is already a specialized type, use that.
   (given that it is compatible with 'compile parameters' XXXX)
pascal
 - If not available, clone the generic type in the tree, and fill in the
   type params with the actual types to use.
   Generate machine code as normal.
   Remember the new specialized type for later in the list by the 
   generic type and type params used.

(the generic type is best written to some ppu as parse tree so that it can be refetched for cloning somewhen. To keep it easy, initially it should be limited to having its .pas file around when wanting to use a generic)

type params

Generics store a list of the type params (names).

text
 ('T', 'Q')

And Specializations store a list of type mappings from the real types to the type params, in the way of

pascal
 type
   T = Integer;
   Q = Boolean;

of course these are local to this class/record specialization.

Other things to note (for later)

What if a method implementation depends on the type used to know how the implementation should look like ?

pascal
 procedure GList<T is TObject>.Add(const value: T);
 procedure GList<T is Integer>.Add(const value: T);
 ?

or more cryptic like

pascal
 procedure GList<TObject>.Add(const value: T);
 procedure GList<Integer>.Add(const value: T);
 ?

or like

pascal
 procedure GList<T>.Add(const value: Integer);
 procedure GList<T>.Add(const value: TObject);

the latter would need extra compiler support but would be nicer, overall. Potentially clashing with normal overloads though.

How to do internal storage classes

pascal
 type
 PNode<T> = ^TNode<T>;
 TNode<T> = record
   prev,next: PNode<T>;
   data: T;
 end;
pascal
 type
 GList<T> = class
 private
   head, tail: PNode<T>;
   cnt: Integer;
 public
   procedure Add(const stuff: T);
 end;
 ?

This implies that generics are best implemented both for records and for classes alike! Also implies that pointer types need to support generic too.

Also one specialization should chain other specializations (probably also when deriving from a generic).

How to derive from the generic type

It would be good if generic types could be derived from.

pascal
 type
 TGenericEventyList<T> = class(TGenericList<T>)
 public
   procedure Add(value: T); override;
 end;
 ...

Interfaces should support generics too

pascal
 type
 IBa<T> = interface
   procedure Add(value: T);
 end;
 TBa<T> = class(...,IBla<T>,IInterface)
 end;

(what to do about the guids of the specializations? probably just generate whatever, they dont have any real meaning after all)

Of course this has limitations to check, like

Limitation of possible specialisations

pascal
 type
 TGenericList<T: TObject or Integer> = ...
 end;

Probably useful...

What to do with class functions

I dont know a whole lot of how class functions are stored internally. What happens to class functions in generics?

'class of'

<Almindor> don't forget class of, we need class of compatibility for dynamic class factories

<Almindor> There are possible problems with 'class of'

Implementation details

Sanity checks

Things to note and not forgot

예:

pascal
 type
 TBla<T> = class
   parent: TBla<T>;
 end;
pascal
 type
 TBla<T> = class
   parent: TBlo<T>;
 end;

Implementation steps

Extend parser to support '<T>' and '<T: bla or bla or bla>' and '<T,Q>' notation

Easy to do. Done.

Different ways to do it, one excluding the other

Way 1

Change class/record/object/pointertype/interface parser to mark if it is a generic, a specialization or normal

Mediate to do. Have done it for class/object and pointertype. Needs all kinds of clone functions for the defs and syms. fpk says he has patches. waiting.

insert dummy symbols (typesym + typedef) for the type params in the generic

Cannot be inserted into the object because parsing of types within objects has been disabled to make that possible:

pascal
 type
 Row = Cardinal;
 TBla = class 
   Row: Integer;
 end;

weird.. (function searchsym_type, is not possible to have type definitions in records, objects, parameters)

parse implementation section methods for the generic

Is harder than it sounds, because the class/object the method is member of has to be searched for the type symbol for the type params (and these not derefed but just used as is).

when parsing generics, use a dummy type in place of the type params

For that, defer the type checking of the compiler in that case to 'as late as possible' or 'never for the generic' <--- TODO... hard...

Change derivation handlers to do the sanity checks

Should pose no problems other than finding all the places.

Add error messages for it

Change pexpr (tnode creating)

<Synopsis+> see the do_resulttypepass() calls in pexpr.pas But the compiler needs to known for example if it allows a . after the type

Examples for postfix operators are:

<Synopsis> grep for m_auto and you'll find the places where it is used <Synopsis> only in pexpr.pas

Change ppu streaming routines to save the tree of generics to the ppu

Change type user to instantiate specialized types for generics when used

Somewhat hard to do. (main work here :))

For the instantiation

Way 2

just keep the source code of the generic around and do blind replacing of the type params and then compile a specialization from that as 'source'

Notes

<Synopsis+> but the code to generate the specialization at runtime can be generated at the time the specialization is created. It doesn't need to be stored in ppu. all information is available to generate the tree at that time.

<Synopsis+> In the 2.1.x development series a rewrite of the ppu loading is planned to make it more clean and more maintainable. Currently it is risky to fix bugs without introducing new bugs

<plugwash-> It needs to be stored in some form so that the specialisations can be generated from it. <Synopsis+> but you can't store them in the ppu. Or with the same limitations as inlining. No references to the implementation symtable.

fpk has patches to make cloning defs possible.

01:28:24 <Synopsis+> users will complain; things like:

pascal
 interface
   generictype declar
 implementation
   procedure helper;
   begin
   end
   constructor generictype.create
   begin
     helper
   end
 end.

Will not be allowed.

Another problem:

pascal
 unit a;
 interface
   generictypeA
 implementation
 uses B
 end.
pascal
 unit b;
 interface
 uses a
 implemenation
 begin
   generictypeA<integer>.craete
 end.

At the time that the implementation of B is parsed it needs already the code of generictypeA. but that is not yet parsed! The compiler doesn't know anything yet. It only knows that generictypeA exists.

<fpk-> dannym, Synopsis: we can simply forbid such code in generics which cause the problem

<fpk-> i.e. everything used by generics must be already included in the interface uses clause ... or forbid references to units used only in the implementation section :)

<Synopsis+> fpk had a good idea that can solve some of the issues: unit bla; interface ... generics ... implementation ... initialisation ... finalizatrion end.

<Synopsis+> The generics delcarations shall be put in a separate section before implementation is parsed

<dannym> at 'procedure TTest.Add(x: T);', T needs to be known. but it doesnt seem that the object symtable is searched for the class method parameters. correct ?

<Synopsis+> dannym: correct, object symtables can not contain types, because that created problems. there is special code for that: remove the objectsymtable before parsing type declarations.

pascal
 { for records, don't search the recordsymtable for the symbols of the types }
 oldsymtablestack:=symtablestack;
 symtablestack:=symtablestack.next;

Because you can have a field with the same name as a type

pascal
 type
 Wrd = Word;
 R = Record
   Wrd : Wrd;
 end

this is allowed. To support this the record or object symtable is remvoed from the symtablestack when the type is parsed.

이미 존재하는 것

메타 클래스

pascal
 type
 TObjectClass = class of TObject;
 TTypedList = class
 private
   Fwanttype: TObjectClass;
   ...
 public
   constructor Create(itemtype: TObjectClass);
   procedure Add(item: TObject);
   ...
 end;
 constructor TTypedList.Create(itemtype: TObjectClass);
 begin
   Fwanttype := itemtype;
   ...
 end;
 procedure TTypedList.Add(item: TObject);
 begin
   assert(item.Class = Fwanttype);
   ...
 end;
pascal
 ...
 TSomeStuff = class
 end;
 ...
 Flist := TTypedList.Create(TSomeStuff);

장점

단점

여기서 부터 통합됩니다

채팅 내용

<Synopsis> http://www.eleforum.com/cgi-bin/eleweb_lift?action=3&script=wall&num=18&reversesort=true&type=today&starttime=00:00&endtime=23:59&startdate=01-02-05&enddate=01-02-05 <Synopsis> or <Synopsis> http://neli.hopto.org:3980/~micha/irc-fpc/2005/01/%23fpc.20050102.log


From the Free Pascal wiki, page "Generics/ko", as it stood on 2010-09-03 after a change by Hwiorn, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 3, last changed by WikiArchive .

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.