Articles   Members Online: 3
-Article/Tip Search
-News Group Search over 21 Million news group articles.
-Delphi/Pascal
-CBuilder/C++
-C#Builder/C#
-JBuilder/Java
-Kylix
Member Area
-Home
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Login/Logout
-Become a Member
-Why sign up!
-Newsletter
-Chat Online!
-Indexes NEW!!
Employment
-Build your resume
-Find a job
-Post a job
-Resume Search
Contacts
-Contacts
-Feedbacks
-Link to us
-Privacy/Disclaimer
Embarcadero
Visit Embarcadero
Embarcadero Community
JEDI
Links
How to List Template In Delphi Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
12-Nov-02
Category
VCL-General
Language
Delphi 2.x
Views
82
User Rating
No Votes
# Votes
0
Replies
0
Publisher:
DSP, Administrator
Reference URL:
DKB
			Author: Jonas Bilinkevicius

How to create a type specific list in Delphi without reimplementing the entired 
list for each type ?

Answer:

The C++ language has a nice feature, that's called Templates. It allowes the 
programer to define a class (or a method) that acts with a none-specific type. At 
complie time, the programer describes for which type the class will be defined. 
That is, you can define a general list (list template) and define all of it's 
methods to work on type A (where 'A' is not defined). For example, the method 
GetItem will look as follows : 

function GetItem(Index: Integer): A;

Then, at compile type you tell the complier that 'A' is actually an Integer, and 
the complier replaces all of the accurances of 'A' with 'Integer'. That way, you 
can write one list (for type 'A'), and each time you wan a list (of Strings, 
Integers, Boolean, Soubles, etc.) you just need to tell the complier to replace 'A' 
with the type you want. 

All of that is very nice, but has nothing to do with Delphi. It's relevent only to 
C++ programers. So what do Delphi programers do ? 

There are 3 majore options. First, write a list of pointers once, and then use it 
many times by passing to it a pointer to the datatype you are interested in. For 
Example : 
   
1   TList = class
2     ...
3     public
4     procedure Add(Value: Pointer);
5   
6     function GetItem(Index: Integer): Pointer;
7     procedure SetItem(Index: Integer; Value: Pointer);
8   
9     property Items[Index: Integer]: Pointer read GetItem write SetItem;
10    
11  end;
12  
13  /Here is the code to use this : 
14  
15  // For Integer;
16  type
17    PInteger = ^Integer;
18  var
19    Item: PInteger;
20    List: TList;
21  begin
22    List := TList.Create;
23    GetMem(Item, SizeOf(Item));
24    Item^ := 1023; // Or what ever value you wish
25    List.Add(Item);
26    ShowMessage(IntToStr(PInteger(List.Items[0])^));
27  end;
28  
29  // For Double;
30  type
31    PDouble = ^Double;
32  var
33    Item: PDouble;
34    List: TList;
35  begin
36    List := TList.Create;
37    GetMem(Item, SizeOf(Item));
38    Item^ := 3.14.15926; // Or what ever value you wish
39    List.Add(Item);
40    ShowMessage(FloatToStr(PDouble(List.Items[0])^));
41  end;


As you've probably noticed there are a few drawbacks to this solution. The most 
obvious one is that you need to typecast the value returned by the List each time 
you want to use it. That might seem as a mere inconvinouce, but if you plan to uses 
lists intensivly, you'll get REALY tired of typecasting all the time. The second 
problem to consider with this design is memory concerns. In the example above, I've 
allocated memory to Item, but never free it. That's because the TList class I've 
used doesn't allocate memory by itself. But then arisses the question, how will 
free the memory ? Probably the TList itself (since the item is now 'owned' by it), 
but that is a bit unconventional, because usually the object (or method) that 
allocates the memory is responsibly to freeing it. You can solve this by writing 
the TList class so it allocates it's own memory and only COPIES the value pointed 
to by Item. But then there are two other problem. 

You need to free the memory of Item after adding it to the List (since the List 
isn't going to free it - it only copied the Items contents). 
You need to find a way of telling TList how many byte to copy. Since TList gets a 
pointer and doesn't know what it points to (a string ? an integer ? a double ?), it 
has no way of knowing how many bytes to copy. 

Those are all very good reasons why NOT to use this solution. Lets have a look at 
the second solution out of the three. 

The second solution is very simple. Write a new list for each type. That is, write 
a TIntergerList, TStringList, TDoubleList, TWhatEverList. Example : 
   
42  TIntegerList = class
43    ...
44    public
45    procedure Add(Value: Integer);
46  
47    function GetItem(Index: Integer): Integer;
48    procedure SetItem(Index: Integer; Value: Integer);
49  
50    proepry Items[Idnex: Integer]: Integer read GetItem write SetItem;
51  end;
52  
53  TDoubleList = class
54    ...
55    public
56    procedure Add(Value: Double);
57  
58    function GetItem(Index: Integer): Double;
59    procedure SetItem(Index: Integer; Value: Double);
60  
61    property Items[Index: Integer]: Double read GetItem write SetItem;
62  end;


The benefits are obvious. You can use a list and have no memory problems and you 
need not typecast ! Implementing these lists could be a little time consuming, but 
if you work a lot with the same types of lists it might be worth while. The only 
draw back of this design (except for a one time developing cost) is it's not 
extendable (at least not easly). That is, if you want to add a new function to your 
List (for example : SaveToFile), you'll have to add the same code for each list you 
implement. That vrings us to the third and final solution. 

This solution is a combination of the first and second solutions. It tries to take 
the best of each. The first solution was very general (worked for every type 
without adding code), but you couldn't make it specific (you have to use 
typecasting inorder to use an Item). The second solution was very specific (no 
typecasting needed) but you had to write a bunch of code for each new list you 
wanted to implement. 

And here is the third solution : Define a base class that is the same as the TList 
in the first solution. Then, for each new list you want (for example : 
TIntegerList) smiply inherite from the base class and add the type specific methods 
(for example : procedure Add(Value : Integer)). There are a few problems with this 
design as well, but I'll discuss them later. For now, lets see why this design 
helps as more than the other two. 

First, it allows you to use type specific lists (no need for typecasting). Second 
it doesn't require you to write a lot of code (five mintues will do) for each new 
List because most of the methods are already implemented and the new methods that 
need to be implemented are very short. 

Lets look closly at the last suggestion. First we need to define a base class : 

63  TBaseList = class
64  protected
65    procedure AddData(Value: Pointer);
66    class function ItemSize: Integer; virtual; abstract;
67  end;
68  
69  procedure TBaseLink.AddData(Value: Pointer);
70  var
71    P: Pointer;
72  begin
73    GetMem(P, ItemSize);
74    Move(P^, Value^, ItemSize);
75    // Here you need to add P to your list.
76    // The way that is done may vary by the way you decide
77    // to save your data. You may want to save it as an Array
78    // or as a linked list, or as a tree, or into a stream, etc.
79  end;
80  
81  //Now, lets create a TIntegerList : 
82  
83  TIntegerList = class
84  protected
85    class function ItemSize: Integer; override;
86  public
87    procedure Add(Value: Integer);
88  end;
89  
90  class fucntion TIntegerList.ItemSize: Integer;
91    begin
92      Result := SizeOf(Integer);
93    end;
94  
95  procedure TIntegerList.Add(Value: Integer);
96  var
97    P: ^Integer;
98    begin
99      GetMem(P, SizeOf(Integer));
100     try
101       P^ := Value;
102       AddData(P);
103     finally
104       FreeMem(P, SizeOf(Integer));
105   end;
106 end;


This example is simplefied. In a real list (with full capabilitys) most of the 
coding is in the base class, and only a few methods are need to be implemented in 
the derived classes. 

I've attached a full implementation of this concept for TIntegerList and 
TStringList. Notice a few things about the attached file : a) The IBooleanList is 
defined but not implemented. b) The marked out methods at the begining of the file 
are not implemented yet. c) Objects aren't suported yet. 

When I finish coding these lists, I'll write another article describing my specific 
implementation of this idea. 

Component Download: http://www.baltsoft.com/files/dkb/attachment/lists.ziphttp://www.baltsoft.com/files/dkb/attachment/lists.zip

			
Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10

 

Advertisement
Share this page
Advertisement
Download from Google

Copyright © Mendozi Enterprises LLC