 |
| Wie kann ich dynamische Programme schreiben? |  |
 |
 |
Was ist damit überhaupt gemeint?
Die tdbengine ist ja bekanntlich Datenbankengine, Interpreter und Compiler in einem.
Sind wir mal froh, dass der in EASY geschrieben Quelltext compiliert
werden kann. Viele potentielle Fehler im Code, die ohne den Compiler
zwangsläufig erst zur Laufzeit vom Interpreter entdeckt würden, würden
uns ein wahres Datenchaos bescheren. Stellen Sie sich vor, mitten im
Backupvorgang entdeckt der Interpreter, dass ein Funktionsparameter vom
falschen Typ übergeben wird und bricht ab. Haben Sie nun ein Backup
oder haben Sie keines?
Manchmal wäre es aber schon nützlich, wenn man die starren Strukturen
etwas auflockern könnte. Dies könnte helfen, Quellcodes etwas
abstrakter zu halten, um sie in anderen Projekten leichter wieder zu
verwenden. Auch würde es uns erlauben, dem Nutzer unserer Anwendungen
mächtige Werkzeuge zur Verfügung zu stellen. In anderen
Datenbanksystemen, wie MS ACCESS, hat man ja schliesslich auch einen
SQL-Editor mit dem man sehr viel (im Rahmen von SQL) anstellen kann.
Zum Glück bietet die tdbengine hier gleich mehrere Möglichkeiten, wie
Sie dynamisch den Programmfluss bestimmen können. Diese möchte ich
Ihnen im Folgenden etwas genauer erklären. DO _"weiterlesen()"
DO _
Mit DO in Kombination mit dem _ (Unterstrich) kann man beliebige Ausdrücke auswerten und anschliessend ausführen.
Beispiel
VAR x : STRING = "cgiwriteln('hallo welt')"
DO _x
Erst
zur Laufzeit, genauer genommen, wenn der Interpreter in der 2. Zeile am
Unterstrich "angekommen" ist, wird der Inhalt der Variablen x
ausgewertet. Hier freilich kein Kunststück, ihr Inhalt ist ja fest
vorgegeben.
DO veranlasst die tdbengine den Ausdruck,
den der Unterstrich _ liefert, auszuführen. In diesem Fall also hätten
wir das ganze sicherlich viel einfacher haben können:
CGIWriteLn('hallo welt')
hätte völlig ausgereicht.
Noch ein Beispiel:
PROCEDURE deletefromadressen(nID : INTEGER)
//exemplarische Löschroutine
...
DelRec(dbADRESSEN, FindAuto(dbADRESSEN, nID))
...
ENDPROC
PROCEDURE deletefromniederlassungen(nID : INTEGER)
...
DelRec(dbNIEDERL, FindAuto(dbNIEDERL, nID))
...
ENDPROC
PROCEDURE delete(cTabelle : STRING; nID : INTEGER)
VAR x : STRING = "deletefrom"+cTabelle+"("+Str(nID)+")"
//werte x zur Laufzeit aus UND "starte" dessen Ergebnis
DO _x
ENDPROC
Nehmen wir an, jede Tabelle in unserem Projekt verlangt gewisse
tabellenspezifische Aufräumarbeiten von uns, wenn Datensätze gelöscht
werden. Es soll jedoch anwendungsweit immer eine eigene Routine
delete() aufgerufen werden. Diese Routine wiederrum soll nun dafür
sorgen, dass die zur jeweiligen Tabelle gehörende deletefrom...()
Prozedur ausgeführt wird. Freilich hätten wir das auch mit einem
IF-THEN-ELSIF-ELSE-ENDMonstrum bewerkstelligen können. Mit jeder neuen
Tabelle aber würde dieser IF-Baum wachsen - bei manchen Projekten ein unter Umständen sehr unübersichtliches Unterfangen.
Im Beispiel sind es die Variablen, die den weiteren Programmablauf bestimmen - und zwar erst zur Laufzeit.
Die Funktion Val()
Val() evaluiert beliebige Strings. Zumindest versucht Val() dies. Auf gut Deutsch macht Val() aus einer Zeichenkette ein Zahl.
Beispiele
VAR c1 : STRING = "123"
VAR c2 : STRING = "123 + 321 + 1 / 2 * 3"
VAR c3 : STRING = "Val(c1) + Val(c2)"
VAR c4 : STRING = "Val('999' + Str(Val(c3),0,2))"
VAR n : REAL
SetPara("nv 0")
n:=Val(c1) // n = 123
n:=Val(c2) // n = 445,5
n:=Val(c3) // n = 568,5
n:=Val(c4) // n = 999568,5
Sie sehen, mit Val()können nahezu beliebige Zeichenketten evaluiert werden. Dabei dürfen
diese sogar auf Funktionen und Variablen des Programms zugreifen.
Allerdings muss dazu der Laufzeitschalter nv auf 0 gestellt werden.
Die Funktion ExecProg()
ExecProg(cFilename : STRING)
Kompiliert und führt beliebige EASY-Programme aus.
cFilename darf dabei sowohl auf eine Datei als auch auf einen ramtext: verweisen.
(Sehr einfaches) Beispiel:
PROCEDURE Main
VAR c : STRING
VAR f : INTEGER = Rewrite("ramtext:code")
WriteLn(f, "Procedure XYZ : String")
WriteLn(f, " RETURN 'xyz'")
WriteLn(f, "Endproc")
WriteLn(f, "")
WriteLn(f, "c := xyz")
Close(f)
ExecProg("ramtext:code")
CGIWriteLn(c)
ENDPROC
Hier wird erst zur Laufzeit ein ramtext:code geschrieben, der anschliessend mit ExecProg() kompiliert und ausgeführt wird.
Interessante
Anwendungsmöglichkeiten stellen hierfür sicherlich dynamische
Selektionsprogramme dar, die erst zur Laufzeit aus Teilbausteinen
individuell zusammengebaut werden. Ein (sehr komplexes)
Anwendungsgebiet wäre hier zum Beispiel die Auswertung und Umsetzung
von SQL-Statements nach EASY-Code.
Weiterführende Links:
/Dokumentation/Laufzeitschalter
Autor: Thomas Friebel <tf@tdb.de>
|