Visit our Sponsor   Visit our Sponsor
delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
500 Users Online NOW
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi







Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (6)


C++-like templates in Delphi Object PascalComponent available for this articleFormat this article printer-friendly!Bookmark function is only available for registered users!
Product:
Delphi all versions
Category:
OO-related
Skill Level:
Scoring:
Last Update:
09/19/2001
Search Keys:
delphi delphi3000 article borland vcl code-snippet C++ templates template list collection design patterns generic algorithm TCollection TCollectionItem TList TObjectList array bag tree hash STL
Times Scored:
26
Visits:
7043
Uploader: Rossen Assenov
Company:
Reference: N/A
Component Download: http://codecentral.borland.com/codecentral/ccweb.exe/listing?id=16429
 
Question/Problem/Abstract:
How to make C++-like templates in Object Pascal.
Answer:






Templates in Object Pascal



Did it happen to you ? You're talking with a C++ programmer about    Delphi
   and how powerful it is, but at the end they usually say something    like
"Ok,   but Delphi uses Pascal and it doesn't support multiple-inheritance
    and templates.   So it is not as good as C++.". Multiple inheritance
is   easy - Delphi has   interfaces and they do the job just fine, but you
have   to agree about templates   - Object Pascal doesn't support them as
a language     feature. Well, guess what  - you can actually implement templates
in Delphi     as good as in C++.

  

              Templates give you the possibility to make generic containers,
  like   lists,   stacks, queues, etc. If you want to implement something
like  this   in Delphi   you have two choices :

  

               1) use a container like TList which holds pointers -
in  this   case   you have to make explicit typecast all the time

               2) subclass a container like TCollection or TObjectList
and   override    all the type-dependent methods each time you want to use
new  data type

  

              A third alternative is to make a unit with generic container
  and each time you want to use it for a new data type you can perform search-and-replace
in the editor.     This will work, but if you change the implementation you
have to change   all  of the units for the different types by hand. It would
be nice if the   compiler  can do the dirty work for you and this is exactly
what we'll do.

  

              Take for example the TCollection / TCollectionItem classes.
When you declare a new  TCollectionItem      descendant you also subclass
a new class from TOwnedCollection  and override      most of the methods so
now they use the new collection item  class type and then  call    the inherited
method with the proper typecast.

  

Here is how to implement a generic collection class template in
3 easy steps :




           1) create a new TEXT file (not an unit file) called
"TemplateCollectionInterface.pas"     :

_COLLECTION_ = class (TOwnedCollection)
protected
function  GetItem (const aIndex : Integer) : _COLLECTION_ITEM_;
procedure SetItem (const aIndex : Integer;
                    const aValue : _COLLECTION_ITEM_);
public
constructor Create (const aOwner : TComponent);

function Add                                 : _COLLECTION_ITEM_;
function FindItemID (const aID    : Integer) : _COLLECTION_ITEM_;
function Insert     (const aIndex : Integer) : _COLLECTION_ITEM_;
property Items      [const aIndex : Integer] : _COLLECTION_ITEM_ read GetItem write SetItem;
end;

               Note that there are no "uses" or "interface" clauses, just
a  generic    type   declaration, where :

  

               a) _COLLECTION_ is the name of the generic  collection
class.

               b) _COLLECTION_ITEM_ is the name of the collection item
subclass     the  collection will hold.

  

2) Create a second TEXT file called "TemplateCollectionImplementation.pas"
     :

constructor _COLLECTION_.Create (const aOwner : TComponent);
begin
inherited Create (aOwner, _COLLECTION_ITEM_);
end;

function _COLLECTION_.Add : _COLLECTION_ITEM_;
begin
Result := _COLLECTION_ITEM_ (inherited Add);
end;

function _COLLECTION_.FindItemID (const aID : Integer) : _COLLECTION_ITEM_;
begin
Result := _COLLECTION_ITEM_ (inherited FindItemID (aID));
end;

function _COLLECTION_.GetItem (const aIndex : Integer) : _COLLECTION_ITEM_;
begin
Result := _COLLECTION_ITEM_ (inherited GetItem (aIndex));
end;

function _COLLECTION_.Insert (const aIndex : Integer) : _COLLECTION_ITEM_;
begin
Result := _COLLECTION_ITEM_ (inherited Insert (aIndex));
end;

procedure _COLLECTION_.SetItem (const aIndex : Integer;
                                const aValue : _COLLECTION_ITEM_);
begin
inherited SetItem (aIndex, aValue);
end;

              Again, there are no "uses" or "implementation" clauses here
-  just   the   implementation  code of the generic type, which is pretty
straight-forward.

  

3) Now,  create a new unit file called "MyCollectionUnit.pas" :

unit MyCollectionUnit;

interface

uses Classes;

type TMyCollectionItem = class (TCollectionItem)
     private
      FMyStringData  : String;
      FMyIntegerData : Integer;
     public
      procedure Assign (aSource : TPersistent); override;
     published
      property MyStringData  : String  read FMyStringData  write FMyStringData;
      property MyIntegerData : Integer read FMyIntegerData write FMyIntegerData;
     end;

     // !!! tell the generic collection class what is the actual collection item class type
    
     _COLLECTION_ITEM_ = TMyCollectionItem;
    
     // !!! insert the generic collection class interface file - preprocessor directive

     {$INCLUDE TemplateCollectionInterface}

     // !!! rename the generic collection class

     TMyCollection = _COLLECTION_;          

implementation

uses SysUtils;

// !!! insert the generic collection class implementation file - preprocessor directive

{$INCLUDE TemplateCollectionImplementation}

procedure TMyCollectionItem.Assign (aSource : TPersistent);
begin
if aSource is TMyCollectionItem then
begin
  FMyStringData  := TMyCollectionItem(aSource).FMyStringData;
  FMyIntegerData := TMyCollectionItem(aSource).FMyIntegerData;
end
else inherited;
end;

end.

              That's it! With only four lines of code the new collection
class    is  ready   and the compiler did all the work for you. If you change
the   interface   or   implementation of the generic collection class the
changes   will propagate     to all the units which use it.

  

One more example. This time we'll implement a generic class    wrapper
   for dynamic arrays.


  

1) Create a new TEXT file named "TemplateVectorInterface.pas" :
_VECTOR_INTERFACE_ = nterface

function  GetLength : Integer;
procedure SetLength (const aLength : Integer);

function  GetItems (const aIndex : Integer) : _VECTOR_DATA_TYPE_;
procedure SetItems (const aIndex : Integer;
                     const aValue : _VECTOR_DATA_TYPE_);

function  GetFirst : _VECTOR_DATA_TYPE_;
procedure SetFirst (const aValue : _VECTOR_DATA_TYPE_);

function  GetLast  : _VECTOR_DATA_TYPE_;
procedure SetLast  (const aValue : _VECTOR_DATA_TYPE_);

function  High  : Integer;
function  Low   : Integer;

function  Clear                              : _VECTOR_INTERFACE_;
function  Extend   (const aDelta : Word = 1) : _VECTOR_INTERFACE_;
function  Contract (const aDelta : Word = 1) : _VECTOR_INTERFACE_;

property  Length                         : Integer             read GetLength write SetLength;
property  Items [const aIndex : Integer] : _VECTOR_DATA_TYPE_  read GetItems  write SetItems; default;
property  First                          : _VECTOR_DATA_TYPE_  read GetFirst  write SetFirst;
property  Last                           : _VECTOR_DATA_TYPE_  read GetLast   write SetLast;
end;

_VECTOR_CLASS_ = class (TInterfacedObject, _VECTOR_INTERFACE_)
private
FArray : array of _VECTOR_DATA_TYPE_;
protected
function  GetLength : Integer;
procedure SetLength (const aLength : Integer);

function  GetItems (const aIndex : Integer) : _VECTOR_DATA_TYPE_;
procedure SetItems (const aIndex : Integer;
                     const aValue : _VECTOR_DATA_TYPE_);

function  GetFirst : _VECTOR_DATA_TYPE_;
procedure SetFirst (const aValue : _VECTOR_DATA_TYPE_);

function  GetLast  : _VECTOR_DATA_TYPE_;
procedure SetLast  (const aValue : _VECTOR_DATA_TYPE_);
public
function  High  : Integer;
function  Low   : Integer;

function  Clear                              : _VECTOR_INTERFACE_;
function  Extend   (const aDelta : Word = 1) : _VECTOR_INTERFACE_;
function  Contract (const aDelta : Word = 1) : _VECTOR_INTERFACE_;

constructor Create (const aLength : Integer);
end;

2) Create a new TEXT file named "TemplateVectorImplementation.pas" :

constructor _VECTOR_CLASS_.Create (const aLength : Integer);
begin
inherited Create;

SetLength (aLength);
end;

function _VECTOR_CLASS_.GetLength : Integer;
begin
Result := System.Length (FArray);
end;

procedure _VECTOR_CLASS_.SetLength (const aLength : Integer);
begin
System.SetLength (FArray, aLength);
end;

function _VECTOR_CLASS_.GetItems (const aIndex : Integer) : _VECTOR_DATA_TYPE_;
begin
Result := FArray [aIndex];
end;

procedure _VECTOR_CLASS_.SetItems (const aIndex : Integer;
                                   const aValue : _VECTOR_DATA_TYPE_);
begin
FArray [aIndex] := aValue;
end;

function _VECTOR_CLASS_.High : Integer;
begin
Result := System.High (FArray);
end;

function _VECTOR_CLASS_.Low : Integer;
begin
Result := System.Low (FArray);
end;

function _VECTOR_CLASS_.GetFirst : _VECTOR_DATA_TYPE_;
begin
Result := FArray [System.Low (FArray)];
end;

procedure _VECTOR_CLASS_.SetFirst (const aValue : _VECTOR_DATA_TYPE_);
begin
FArray [System.Low (FArray)] := aValue;
end;

function _VECTOR_CLASS_.GetLast : _VECTOR_DATA_TYPE_;
begin
Result := FArray [System.High (FArray)];
end;

procedure _VECTOR_CLASS_.SetLast (const aValue : _VECTOR_DATA_TYPE_);
begin
FArray [System.High (FArray)] := aValue;
end;

function _VECTOR_CLASS_.Clear : _VECTOR_INTERFACE_;
begin
FArray := Nil;

Result := Self;
end;

function _VECTOR_CLASS_.Extend (const aDelta : Word) : _VECTOR_INTERFACE_;
begin
System.SetLength (FArray, System.Length (FArray) + aDelta);

Result := Self;
end;

function _VECTOR_CLASS_.Contract (const aDelta : Word) : _VECTOR_INTERFACE_;
begin
System.SetLength (FArray, System.Length (FArray) - aDelta);

Result := Self;
end;

3) Finally, create UNIT file named "FloatVectorUnit.pas" :
unit FloatVectorUnit;

interface

uses Classes;                           // !!! "Classes" unit contains TInterfacedObject class declaration

type _VECTOR_DATA_TYPE_ = Double;       // !!! the data type for the array class is Double

     {$INCLUDE TemplateVectorInterface}

     IFloatVector = _VECTOR_INTERFACE_; // !!! give the interface a meanigful name
     TFloatVector = _VECTOR_CLASS_;     // !!! give the class a meanigful name

function CreateFloatVector (const aLength : Integer = 0) : IFloatVector; // !!! this is an optional factory function

implementation

{$INCLUDE TemplateVectorImplementation}

function CreateFloatVector (const aLength : Integer = 0) : IFloatVector;    
begin
Result := TFloatVector.Create (aLength);
end;

end.

             You can also easily extend the generic vector class with iterators
    and   some  additional functions.

  

              And here is how you can use the new vector interface :
procedure TestFloatVector;
var aFloatVector : IFloatVector;
     aIndex       : Integer;
begin
aFloatVector := CreateFloatVector;

aFloatVector.Extend.Last := 1;
aFloatVector.Extend.Last := 2;

for aIndex := aFloatVector.Low to aFloatVector.High do
begin
  WriteLn (FloatToStr (aFloatVector [aIndex]));
end;
end.

              The only requirements when implementing templates this way
is  that   each   new type should be declared in a separate unit and you
should  have   the sources   for the generic classes.



Suggestions and comments are welcomed at : rossen_assenov@yahoo.com.

  

  







Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
How...
    José Guilherme Simão Prado (Apr 16 2007 10:49PM)

can I use different types of list in the same unit. I want to declare a list of type A and another one of type B.

thanks

Respond

RE: How...
An Dee (May 4 2007 2:56PM)

You can try next one solution
http://deex.delphist.com/
with DTL library page
Respond

What about STL algorithms?
    anonymus (Jul 11 2004 12:48PM)

Does anybody have implemented such algotihms like unique and sort from STL library?
Respond

Excellent!
    Marcus Neves (Oct 17 2001 1:46PM)

The best ideas are always simple. The problem is to find them. You´ll find a wonderful solution to a very good C++-like feature to use them on Delphi. Thanks very much!
Respond

Hey...
    William Egge (Sep 5 2001 5:50AM)

I like that. Never thought of doing anything similar to that. Thanks for the idea.
Respond

RE: Hey...
Peter Morris (Sep 5 2001 10:21AM)

A very good plan.  The only thing I had considered doing myself was to write a wizzard.  Not sure which is the best approach but I still like yours anyway.

Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
E. Irigoyen
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)