GNU-Pascal in Beispielen: Pascal und C
zurück zu GNU-Pascal in Beispielen
Pascal und C
BearbeitenViele interessante Programmbibliotheken sind in der Programmiersprache „C“ geschrieben. Wie diese Bibliotheken in GNU-Pascal genutzt werden können, möchte dieses Kapitel vermitteln.
C-Funktionen in Pascal-Programmen
BearbeitenDas einführende Beispiel beschreibt, wie man eine einfache C-Funktion [1] in ein GNU-Pascal-Programm einbindet:
Programm: hallo.c
Bearbeiten/* hallo.c */
#include <stdio.h>
void print_hallo (void)
{
printf ("Hallo, Welt!\n");
}
Das aufrufende GNU-Pascal-Programm sieht so aus:
Programm: PasC1
Bearbeitenprogram PasC1;
procedure PrintHallo; external; attribute (name='print_hallo');
begin
PrintHallo
end.
Erklärung
BearbeitenDie beiden Quelltexte müssen mit gpc hallo.c pasc1.pas -o pasc1 übersetzt werden, das Aufrufen des Programmes erzeugt die erwartete Ausgabe. Die Prozedur PrintHallo wird hier als external deklariert, was bedeutet, dass sie irgendwo definiert ist, wir uns aber für den Ort, wo sie definiert ist nicht näher interessieren. Das Attribut name legt fest, dass der vom Compiler intern vergebene Name dieser Prozedure der Gleiche ist, wie der unserer C-Funktion. Dadurch wird PrintHallo zu einem Synonym für print_hallo. Intern verweisen beide Funktionen auf denselben Namen und dadurch auf denselben Code.
C-Funktionen in Pascal-Units
BearbeitenBei größeren Sammlungen von C-Routinen empfiehlt es sich, diese Routinen in Units einzubetten. Wir benutzen dazu wieder obigen C-Quelltext als Grundlage:
Programm: HalloC
Bearbeitenunit HalloC;
interface
procedure PrintHallo; external; attribute (name='print_hallo');
implementation
{$L hallo.c}
end.
Das aufrufende Programm sieht so aus:
Programm: PasC2
Bearbeitenprogram PasC2;
uses HalloC;
begin
PrintHallo
end.
Erklärung
BearbeitenDie Übersetzung des Programmes vereinfacht sich nun zu gpc --automake pasc2.pas -o pasc2, da der C-Quelltext in die Unit mit Hilfe von {$L hallo.c} eingebunden wird.
Strukturen und Funktionen
BearbeitenDas Teilen von Strukturen und Funktionen zwischen verschiedenen Sprachen ist zumeist der Hauptzweck für das mehrsprachige Programmieren. Für einfache Typen gibt es folgende Übersetzungshilfe:
C-Datentyp | GNU-Pascal-Datentyp |
---|---|
char * | CString |
(unsigned) char | ByteInt (ByteCard) |
(unsigned) short int | ShortInt (ShortCard) |
(unsigned) int | CInteger (CCardinal) |
(unsigned) long int | MedInt (MedCard) |
(unsigned) long long int | LongInt (LongCard) |
float | ShortReal |
double | Real |
Records lassen sich aus den vorhandenen C-Strukturen übernehmen. Bei Konstanten und Aufzählungstypen verwenden wir Variablen, denen das Attribut const nachgestellt wird.
Funktionsparameter werden ähnlich übersetzt, wobei Referenzparameter in C wie Zeiger behandelt werden. Alle C-Funktionen, die void zurückliefern, können in Pascal als Prozeduren implementiert werden.
Wrapper schreiben
BearbeitenWrapper sind Funktionen, die in der Quellsprache geschrieben werden und die eigentlich zu übernehmenden Funktionen überlagern. Dadurch wird selbst bei einem sich ändernden Parameter- oder Rückgabetyp der Originalfunktion der Aufruf aus einer Pascal-Routine nicht beeinträchtigt. Dieser Vorteil ist mit einem kleinen Nachteil verbunden: Es werden nunmehr zwei zusätzliche Dateien benötigt, die Pascal-Unit und der Wrapper Quelltext.
C-Datei: mathe.c
Bearbeiten /* mathe.c */
int addiere (int a, int b)
{
return a + b;
}
Auf die Datei mathe.c hat man üblicherweise keinen Zugriff, sie versteckt sich innerhalb veröffentlichter Programmbibliotheken. Die öffentliche Schnittstelle zu solchen Funktionen ist die Header-Datei:
C-Datei: mathe.h
Bearbeiten /* mathe.h */
int addiere (int a, int b);
Die Wrapper-Datei sieht wie folgt aus:
C-Datei: mathec.c
Bearbeiten/* mathec.c */
#include "mathe.c"
int _c_addiere (int a, int b)
{
return (int) addiere (a, b);
}
Die Unit, welche die Funktionen aus mathec.c benutzt folgt nun:
Unit: Mathe
Bearbeitenunit Mathe;
interface
function Addiere (a, b: Integer): Integer; external name '_c_addiere';
implementation
{$L mathec.c}
end.
Erklärung
BearbeitenDie Schreibweise external name '_c_addiere' ist eine Kurzschreibweise für external; attribute (name='_c_addiere') . Die C-Funktion addiere (int a, int b) darf sich nun in gewissen Grenzen ändern, ohne dass das Pascal-Interface davon berührt wird. Selbst wenn sie in zukünftigen Versionen der Mathematik-Bibliothek ganz wegfallen sollte, so braucht sie nur noch in der Wrapper-Datei neu berücksichtigt zu werden.
Übersetzen einer Bibliothek
BearbeitenDas Übersetzen einer Bibliothek sollte mit dem bis hierher erworbenen Wissen leicht fallen. Es handelt sich dabei um einen kleinen Ausschnitt der Bibliothek GTK. Hier folgt ein Testprogramm aus dem Tutorial der GTK:
C-Programm: gtk_test.c
Bearbeiten /* gtk_test.c */
#include <gtk/gtk.h>
int main(int argc, char *argv[])
{
GtkWidget *window;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_show (window);
gtk_main ();
return 0;
}
Das Programm enthält einen Typ (GtkWidget), eine Konstante (GTK_WINDOW_TOPLEVEL) und eine Reihe von C-Funktionen, die es nach Pascal zu übersetzen gilt.
GtkWidget wird in der gesamten Biblothek nur als Zeiger verwendet, weswegen für diesen Typ der allgemeine Pointer in Frage kommt.
Die erste Schwierigkeit taucht bei gtk_init (&argc, &argv) auf. Pascal verwendet nicht denselben Mechanismus, um Programmparameter zu referenzieren wie C. Zu diesem Zweck wurden in GNU-Pascal eigene Variablen eingefügt, welche CParamCount und CParameters heissen. Diese Variablen ersetzen die üblichen Parameter argc und argv der main-Funktion.
Die Konstante GTK_WINDOW_TOPLEVEL ist in der C Header-Datei gtkenums.h als Aufzählungstyp definiert:
/* Window types */
typedef enum
{
GTK_WINDOW_TOPLEVEL,
GTK_WINDOW_POPUP
} GtkWindowType;
Da sich prinzipiell ihr Wert zwischen zwei Versionen ändern kann, müssen wir sie durch eine Wrapper-Konstante umfassen:
C-Datei: gtkc.c
Bearbeiten/* gtkc.c */
#include <gtk/gtk.h>
/* Wrapper für Konstanten */
const int _c_GTK_WINDOW_TOPLEVEL = GTK_WINDOW_TOPLEVEL;
const int _c_GTK_WINDOW_POPUP = GTK_WINDOW_POPUP;
Die oben genannten Überlegungen fassen wir nun in eine Unit zusammen:
Unit: GTK
Bearbeitenunit GTK;
interface
type
GtkWidget = Pointer;
var
GTK_WINDOW_TOPLEVEL: CInteger;
external; attribute (name = '_c_GTK_WINDOW_TOPLEVEL', const);
GTK_WINDOW_POPUP : CInteger;
external; attribute (name = '_c_GTK_WINDOW_POPUP', const);
procedure GtkInit;
procedure GtkMain; external name 'gtk_main';
procedure GtkWidgetShow (Widget: GtkWidget);
external name 'gtk_widget_show';
function GtkWindowNew (WindowType: CInteger): GtkWidget;
external name 'gtk_window_new';
implementation
{$L gtkc.c}
uses GPC;
procedure CGtkInit (ArgC: Pointer; ArgV: Pointer);
external name 'gtk_init';
procedure GtkInit;
begin
CGtkInit (@CParamCount, @CParameters)
end;
end.
Erklärung
BearbeitenDa die beiden Konstanten GTK_WINDOW_TOPLEVEL und GTK_WINDOW_POPUP mehr oder weniger eine Einheit bilden haben wir sie zusammen aufgeführt und in die Unit übernommen. Diese beiden Konstanten wurden als Variablen eingebunden, die einen Konstanten Wert haben und extern deklariert wurden.
Die Prozedur GtkInit musste im Implementationsteil zweimal aufgeführt werden. Einmal in ihrer "Originaldarstellung" und einmal so, wie wir sie benutzen wollen, nämlich ohne Argumente. Dies können wir hier tun, da diese Prozedur immer mit denselben Argumenten aufgerufen wird.
Das Programm, welches diese Unit testet sieht wie folgt aus:
Programm: GTKTest
Bearbeitenprogram GTKTest;
uses GTK;
var
Window: GtkWidget;
begin
GtkInit;
Window := GtkWindowNew (GTK_WINDOW_TOPLEVEL);
GtkWidgetShow (Window);
GtkMain
end.
Dieses Programm ist die direkte Übersetzung des am Anfang des Abschnitts in C vorgeführten Quelltextes und wird wie folgt übersetzt:
gpc --automake gtktest.pas -o gtktest `pkg-config gtk+-2.0 --cflags --libs`
Ausblick
BearbeitenDas Übersetzen von Bibliotheken ist eine sehr dankbare Aufgabe. Wenn sie sich ihr gewachsen fühlen und tatsächlich die GTK oder eine andere Bibliothek für GNU-Pascal benutzbar machen wollen, so melden Sie sich bitte auf der Mailingliste. Andere Bibliotheken, die von Interesse sein könnten sind ALSA und libsndfile.
Anmerkungen
Bearbeiten- ↑ In der Programmiersprache "C" wird nicht zwischen Funktionen und Prozeduren unterschieden. In C ist jede Routine eine Funktion.