allegro Klassenbibliothek in C++

2014-04-11

Die Klassenbibliothek ist eine Entwicklung, die für den Umgang mit dem allegro-Konzept drei Ebenen eröffnet:

  1. Entwicklungsarbeit: ein plattformunabhängiges Kernsystem in objektorientierter Methodik.
    Hier könnte man z.B. neue Satz- und Indexstrukturen und -funktionen in C++ entwickeln oder diese erweitern und verbessern.
  2. Anwendungsprogrammierung: ein Entwicklungssystem (Programmierschnittstelle), aufbauend auf dem Kernsystem, für die Gestaltung neuer Anwendungen auf der Basis von allegro-Datenbanken. Beispiele: a99/alcarta, acon
  3. Anwendung: mehr Komfort durch Einbindung in graphische Benutzeroberflächen nach gängigen Standards. Beispiele: Web-Schnittstellen mit PHP, Perl o.a., FLEX-Funktionspakete für a99/alcarta, RIA-Programme wie z.B. a30 und a35 .
    Mit FLEX wird die Klassenbibliothek auf höherer Ebene indirekt genutzt.

Das Kernsystem in C++ enthält nur die folgenden fünf Klassen (= Objektbeschreibungen):

KONFIG
Konfiguration. Nimmt praktisch den Inhalt einer Konfigurationsdatei (.cfg) auf.
Diese Klasse enthält "Methoden" (auch "Memberfunktionen" genannt), um Datenfelder zu überprüfen, d.h. anhand der Kategorieliste ihre formale Korrektheit festzustellen: ist es eine zulässige Kategorie, enthält sie unerlaubte Teilfelder?, usw.

RECORD  [Hier ist als Beispiel der Quelltext  record.hpp  zu sehen]
Datensatz. Dient zur Darstellung von Sätzen aus der Datenbank im Arbeitsspeicher.
Wichtige Methoden sind das Ändern von Feldern und das Einordnen neuer Datenfelder in den Datensatz, auch das Löschen von Kategorien und hierarchischen Untersätzen. Mit einem Aufruf  Satz->Ins(field)  
ordnet man z.B. eine Zeichenfolge  field  in einen RECORD-Objekt  Satz  ein. (Die Zeichenfolge muß mit einer Kategorienummer beginnen, z.B. #20. Ist dieses Feld schon im Satz vorhanden, wird es überschrieben.)

EXET
Export-Set. Nimmt eine Export-Parameterdatei auf, die eine genaue Vorschrift für das Exportieren von Datensätzen enthält, formuliert in der sog. Exportsprache. Alle Outputs und auch die Indexeinträge werden mit dieser Sprache parametriert.
Die wichtigste Methode ist die Funktion Exp(), die den Export eines Satzes vorschriftsgemäß ausführt. Man übergibt also dieser Funktion mit dem Aufruf  Exp(Satz)  ein Objekt der Klasse RECORD, um diesen Datensatz exportieren zu lassen.

INDEX
Indexdatei. Diese Klasse erweitert die EXET-Klasse. Ein Objekt dieser Klasse stellt den Index einer Datenbank dar.
Es gibt eine Anzahl Methoden (Funktionen), die den Zugriff auf die Register ermöglichen, aber auch die Berechnung aller Schlüssel eines Datensatzes sowie das Ausführen von einfachen und trunkierten Suchbefehlen. 
Auf einer tieferen Ebene liegen die elementaren Funktionen für den Umgang mit der Indexdatei und den Schlüsseln. Diese Funktionen braucht der Programmierer nicht zu kennen, sie sind auf der Klassenebene unsichtbar. Die Quellen liegen vor in einem Paket  aindex, das zu einer Library kompiliert wird.

ABASE
allegro-Datenbank. Diese Klasse erweitert die INDEX-Klasse.
Hier gibt es Methoden, um Sätze zu laden, zu löschen, und bearbeitete Sätze zurückzuspeichern, einschließlich aller Indexveränderungen:
Mit dem einfachen Befehl  Put(Satz)  wird das RECORD-Objekt Satz gespeichert, mit  Del(satznummer)  wird er gelöscht.
Diese Klassen bilden zusammen ein "Application Programmer Interface" (API) für die Sprache C++. Die Klassen sind genauestens beschrieben in Dateien mit den Namen KONFIG.HPP, RECORD.HPP usw. Der Programmierer kann daraus alle Strukturen und Methoden ersehen, die in einem C++-Programm verwendet werden können. Diese Dateien sind also zugleich die API-Dokumentation. Da diese Klassen keine interaktiven Teile enthalten (d.h. keine Tastatur- und Bildschirmprozeduren), sind sie mit beliebigen Klassenbibliotheken für GUI-Gestaltungen, aber vor allem für Client/Server-Anwendungen, kombinierbar. Das allegro-Datenbanksystem wird damit auf Programmierebene zu einem absolut offenen System.

Hinsichtlich der Datenstrukturen und der Funktionen auf Satz- und Indexebene sind die Klassen kompatibel mit den älteren DOS-Programmen. Daher können auch alte Datenbanken ohne irgendeine Umwandlung von neuen Programmen benutzt und verarbeitet werden.


Beispiel für ein objektorientiertes Programm

Es folgt nun ein kleines Musterprogramm auf Basis der Klassenbibliothek, welches nur zeigen soll, wie die genannten Klassen eingesetzt werden. 

Das Programm  test.cpp  macht folgendes:

  • es sucht über Register 3 alle Datensätze, die ein mit "biochem" beginnendes Stichwort enthalten,
  • ordnet ein neues Feld #30a mit Inhalt "BTC" in diese Sätze ein,
  • exportiert sie mittels einer Parameterdatei D-SHOW.APR auf den Bildschirm,
  • und speichert sie wieder zurück (wobei die Indexbearbeitung automatisch erfolgt).

Empfehlung:
Zum praktischen Ausprobieren wird test.cpp nicht empfohlen. Dazu gibt es ein etwas größeres, das flexibler ist und damit auch praktisch nützlich sein kann: osdp.cpp. Man findet die Quelldatei im SVN.  Es enthält einige Kommentare, markiert mit ++++, die den individuellen Ausbau erleichtern sollen. So kommt man am schnellsten zu einem eigenen Konsolprogramm mit Zugriff auf allegro-Datenbanken. 

Um die Lesbarkeit zu verbessern, sind hier Kommentare kursiv gesetzt, der eigentliche Programmtext fett.
Das Programm ist in der Sprache C++ geschrieben. Das Symbol // leitet Kommentare ein, d.h. der Rest der Zeile wird vom Compiler ignoriert.

// Musterprogramm  test.cpp

// Allgemeine Vorbereitungen:
// Klassenbeschreibungen:

// die Datei abase.hpp enthält alle Beschreibungen

#include "abase.hpp"

// Hier beginnt das Programm:

int Amain()
{
if(!uifRead('e')) A_EXIT(10); // Lies die Datei uifeger, Abbruch falls Fehler

// Zuerst wird $a.CFG geladen, und zwar vom Verzeichnis c:\allegro\demo2 :
// (Wenn $a.cfg nicht existiert, wird a.cfg gesucht. Dies geht auf V13 zurück)
// (eine Konfiguration wird immer gebraucht)

KONFIG *kfg=new KONFIG("a","c:\\allegro\\demo2\\");

if(*Aerror) { printf("%s\n",Aerror); A_EXIT(10); } // Fehler in a.cfg?

// Fehlermeldungen solcher Funktionen stehen immer in der Variablen Aerror
// if(*Aerror) bedeutet: steht etwas in Aerror? Wenn ja, wird die
// nachfolgende Klammer ausgeführt
// A_EXIT(10) heisst: Programm wird mit Fehlercode 10 beendet

// Im folgenden wird jeweils die Konfiguration kfg benutzt, um Objekte
// zu erzeugen, d.h. kfg ist ein Objekt der Klasse KONFIG

// Exportparameter d-show.apr werden geladen in ein Objekt der Klasse EXET,
// welches den Namen Expo bekommt:


EXET *Expo=new EXET("d-show",kfg,0, "c:\\allegro\\demo2\\");

if(*Aerror) { printf("Aerror=%s\n",Aerror); A_EXIT(10); }

// zum Ausgeben soll die Funktion putchar() dienen, d.h. Ausgabe auf Bildschirm;

// deshalb wird die Adresse dieser Funktion an das Objekt Expo uebergeben:


Expo->Outfunc(putchar);

// Ein Objekt RECORD (Datensatz) wird geschaffen, es beruht gleichfalls auf kfg:


RECORD *Satz=new RECORD(kfg);


// Die Datenbank cat auf c:\allegro\demo2 wird geoeffnet:
// (d.h. ein Objekt der Klasse ABASE wird angelegt)
// Dafür wird kfg verwendet, d.h. man koennte auch eine andere Version der CFG nehmen

ABASE *Bank=new ABASE("c:\\allegro\\demo2\\","cat", kfg,1);

// und wieder: wenn in Aerror was steht, hat es nicht geklappt
if(*Aerror) { printf("Fehler beim Öffnen der Datenbank: %s\n",Aerror); A_EXIT(10); }

// Jetzt wird mit den erstellten Objekten einiges angestellt.
// ein paar Variablen werden gebraucht:

CHAR txt[256]; // soll den Text einer Kurztitelzeile aufnehmen
long results[10000]; // Ergebnismenge: bis zu 10000 Satznummern
long rs; // Anzahl der Ergebnisse

// Im Register 3 wird nach Eintraegen gesucht, die mit "biochem" anfangen:
// Die Satznummern werden in results eingetragen:

rs=Bank->ADXget(3,"biochem",results,1);
// Die Ziffer 1 schaltet den Modus "trunkierte Suche" ein
// Der Wert rs ist 0, wenn nichts gefunden wurde, -1, wenn ein Fehler auftrat
// (ein Fehlerhinweis steht dann in Aerror)
if(rs==0) { printf("Nichts gefunden\n"); return 0; }
if(rs<0) { printf("Fehler: %s\n",Aerror); EXIT(10); }

// Am Bildschirm wird angezeigt, wieviele Ergebnisse (rs) es waren:

cout << endl << "Ergebnisse : " << rs << endl;

// "endl" heisst "end of line" und erzeugt einen Zeilenvorschub
// Alle Saetze der Ergebnismenge werden jetzt als Kurztitel (entnommen
// aus der .STL-Datei) angezeigt, dann geladen und mit dem
// Exportobjekt Expo exportiert (gleichfalls auf den Bildschirm)

long i=0; // Variable fuer die laufende Nummer
while(i<rs)
{
cout << results[i] ; // Interne Satznummer anzeigen

// Kurztitelzeile holen, in txt zwischenspeichern:

k=Bank->STLget(results[i],txt);

// und dann anzeigen: (die C-Funktion printf() wird benutzt)

printf(", Kurztitel: %s\n",txt);

// Datensatz in das Objekt Satz holen:

Bank->Get(results[i++],Satz,0);

// Kategorie #30a mit Inhalt BTC in den Satz einordnen:

Satz->Ins("#30aBTC");

// und dann den Satz mittels Expo exportieren:
// (ein Objekt der Klasse EXET hat eine "Memberfunktion" Exp(), die den
// eigentlichen Export ausfuehrt, d.h. die Parameter abarbeitet)

Expo->Exp(Satz);
 // Alternativ, wenn V14-Aufloesungen noetig sind:
// Bank->ExpV14(Satz,Expo);

// Datensatz speichern (Indexbearbeitung erfolgt dabei automatisch)
Bank->Put(Satz);

if(*Aerror) { printf("Speicherung gescheitert: %s\n",Aerror); A_EXIT(10); }


} // Ende der while()-Schleife

return 1; // zurueck zu main(), Beendigung des Programms

}

FLEX macht alles viel einfacher


Weil man nicht für jede spezielle Aufgabe ein eigenes C++-Programm schreiben kann, wurde eine höhere Ebene geschaffen: FLEX.
Die Programme a99 und acon können (mit einigen Unterschieden) FLEX-Skripte ausführen.
Solche Skripte werden zur Laufzeit in Funktionen der oben gezeigten Art umgesetzt, d.h. man braucht von der Klassenbibliothek dann gar nichts zu wissen.
Der oben dargestellte Vorgang könnte in FLEX für a99 so programmiert werden:
 
find |3 biochem?
if empty end
export p d-show
export f protokoll.rtf
get first
:loop
var "BTC"
insert #30a
put
if no write "Satz " i " konnte nicht gespeichert werden" n
write s n
export
next
if yes jump loop
help protokoll.rtf
end
Wer also eigene Funktionen braucht, die von acon nicht abgedeckt sind, braucht kein komplettes C++-Programm 
mit der Klassenbibliothek neu zu schreiben, sondern könnte z.B. acon heranziehen und es um neue Befehle erweitern.
acon ist ein plattformunabhängiges Konsolprogramm in C++, kann also auch z.B. sofort für Linux und Solaris kompiliert werden.
Es erledigt u.a. auch für den avanti-Server die Bearbeitung der Aufträge, und es ersetzt mit dem speziellen Skript update.job das alte DOS-Programm update.exe.



B.Eversberg, 2009-10-26 / 2012-06-18