Articles   Members Online:
-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 call procedures by name using an array of records 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
30-Aug-02
Category
Algorithm
Language
Delphi 2.x
Views
172
User Rating
No Votes
# Votes
0
Replies
0
Publisher:
DSP, Administrator
Reference URL:
DKB
			Author: Tomas Rutkauskas

I have a unit that all it does is store SQL statement for me to load and right now 
I'm doing:

1   if ReportName = "Some_Report_Name" then
2     LoadSomeReportNameSql;
3   else if ReportName = "Some_Other_Report" then
4     LoadSomeOtherReportSql;


I have about 200 reports so far...would a case statment be faster? I would, of 
course, change the identifier for the report to a numeric identifier, rather than a 
string identifier. My concern is that there will begin to be a very noticable 
difference once I get up to the 500 or so reports.

Answer:

With that many reports, there are two better solutions than an if/ then or case 
statement.


Solve 1:

An array of records containing the report name and report procedure might be faster 
and easier to maintain. The list could be sorted on the report name, and a binary 
search algorithm could be used to quickly locate the correct report procedure to 
execute.

This method is not new, but works very well. It is not automagical, so the 
programmer has to do some typing. It could be improved in a myriad of ways, like 
array of const parameters, TVarRec results, action identifers and encapsulation in 
a class. The last could get hairy if you expect that class to serve objects of 
other classes as well, but it is possible.


5   unit NamedFunctions;
6   
7   interface
8   
9   const
10    MaxFuncs = 3;
11    MaxFuncName = 13;
12  
13  type
14    TFuncRange = 1..MaxFuncs;
15    TNamedFunc = function(args: string): string;
16    TFuncName = string[MaxFuncName];
17    TFuncInfo = record
18      Name: TFuncName;
19      Func: TNamedFunc
20    end; { TNamedFunc }
21  
22    TFuncList = array[TFuncRange] of TFuncInfo;
23  function XSqrt(args: string): string;
24  function XUpStr(args: string): string;
25  function XToggle(args: string): string;
26  
27  const
28    {This list must be sorted for the function to be found}
29    FuncList: TFuncList = ((Name: 'xsqrt'; Func: XSqrt), (Name: 'xtoggle'; Func: 
30  XToggle), (Name: 'xupstr'; Func: XUpStr));
31  
32  function ExecFunc(AName: TFuncName; args: string): string;
33  
34  implementation
35  
36  uses
37    Dialogs, SysUtils;
38  
39  function ExecFunc(AName: TFuncName; args: string): string;
40  { Binary search is overkill for a small number of functions. }
41  var
42    CompRes, i, j, m: integer;
43    Found: boolean;
44  begin
45    AName := LowerCase(AName);
46    i := 1;
47    j := MaxFuncs;
48    m := (i + j) shr 1;
49    Found := false;
50    while not Found and (i <= j) do
51    begin
52      CompRes := AnsiCompareStr(AName, FuncList[m].Name);
53      if CompRes < 0 then
54        j := m - 1
55      else if CompRes > 0 then
56        i := m + 1
57      else
58        Found := true;
59      if not Found then
60        m := (i + j) shr 1
61    end;
62    if Found then
63      Result := FuncList[m].Func(args)
64    else
65    begin
66      Result := '';
67      ShowMessage('Function ' + AName + ' not found in list')
68    end;
69  end;
70  
71  function XSqrt(args: string): string;
72  var
73    value: real;
74  begin
75    value := 0;
76    try
77      value := StrToFloat(args)
78    except
79      on EConvertError do
80        ShowMessage(args + ' is not a valid real number (XStr)')
81    end;
82    if value >= 0 then
83      Result := FloatToStr(sqrt(value))
84    else
85    begin
86      Result := '0.0';
87      ShowMessage('Negative number passed to XSqrt')
88    end;
89  end;
90  
91  function XUpStr(args: string): string;
92  begin
93    Result := UpperCase(args)
94  end;
95  
96  function XToggle(args: string): string;
97  { Anything other than 'TRUE' or 'T' is assumed false. }
98  begin
99    args := UpperCase(args);
100   if (args = 'TRUE') or ((length(args) = 1) and (args = 'T')) then
101     Result := 'FALSE'
102   else
103     Result := 'TRUE'
104 end;
105 
106 end.



Solve 2:

Another way to go would be to use the GetProcAddress Win32 API function to locate 
the report procedure based on the report name. This way you could store the report 
names and report procedure names in a text file or database. (Tip: EXEs can export 
routines just like DLLs can. GetProcAddress only finds exported routine names). The 
code might look something like this (off the top of my head...):


107 unit MyReports;
108 
109 interface
110 
111 type
112   TReportProcedure = procedure;
113 
114 procedure LoadSomeReportNameSql;
115 procedure LoadSomeOtherReportSql;
116 procedure ExecuteReport(AReportName: string);
117 
118 implementation
119 
120 procedure ExecuteReport(AReportName: string);
121 var
122   ReportProc: TReportProcedure;
123   ProcPointer: TFarProc;
124 begin
125   {Table contains two columns: "Report Name" and "Report Procedure".  Primary key 
126 is "Report Name"}
127   try
128     Table1.Open;
129     if Table1.FindKey([AReportName]) then
130     begin
131       {Get the address of the exported report procedure}
132       ProcPointer := GetProcAddress(HInstance, Table1.FieldByName('Report 
133 Procedure');
134       if Assigned(ProcPointer) then
135       begin
136         ReportProcedure := TReportProcedure(ProcPointer);
137         ReportProcedure;
138       end;
139     end;
140   finally
141     Table1.Close;
142   end;
143 end;
144 
145 procedure LoadSomeReportNameSql;
146 begin
147 end;
148 
149 procedure LoadSomeOtherReportSql;
150 begin
151 end;
152 
153 exports
154   LoadSomeReportNameSql;
155 LoadSomeOtherReportSql;
156 
157 end.


			
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