GNU-Pascal in Beispielen: Pascal und C

zurück zu GNU-Pascal in Beispielen

Pascal und CBearbeiten

Viele interessante Programmbibliotheken werden in der Programmiersprache "C" geschrieben. Wie diese Bibliotheken in GNU-Pascal genutzt werden können, möchte dieses Kapitel vermitteln.

C-Funktionen in Pascal-ProgrammenBearbeiten

Das einführende Beispiel beschreibt, wie man eine einfache C-Funktion [1] in ein GNU-Pascal-Programm einbindet:

Programm: hallo.cBearbeiten

/* hallo.c */
#include <stdio.h>

void print_hallo (void)
{
  printf ("Hallo, Welt!\n");
}

Das aufrufende GNU-Pascal-Programm sieht so aus:

Programm: PasC1Bearbeiten

program PasC1;

procedure PrintHallo; external; attribute (name='print_hallo');

begin
  PrintHallo
end.

ErklärungBearbeiten

Die 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-UnitsBearbeiten

Bei größeren Sammlungen von C-Routinen empfiehlt es sich, diese Routinen in Units einzubetten. Wir benutzen dazu wieder obigen C-Quelltext als Grundlage:

Programm: HalloCBearbeiten

unit HalloC;

interface

procedure PrintHallo; external; attribute (name='print_hallo');

implementation
{$L hallo.c}

end.

Das aufrufende Programm sieht so aus:

Programm: PasC2Bearbeiten

program PasC2;

uses HalloC;

begin
  PrintHallo
end.

ErklärungBearbeiten

Die Ü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 FunktionenBearbeiten

Das 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 schreibenBearbeiten

Wrapper 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.cBearbeiten

 /* 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.hBearbeiten

 /* mathe.h */
 int addiere (int a, int b);

Die Wrapper-Datei sieht wie folgt aus:

C-Datei: mathec.cBearbeiten

/* 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: MatheBearbeiten

unit Mathe;

interface

function Addiere (a, b: Integer): Integer; external name '_c_addiere';

implementation

{$L mathec.c}

end.

ErklärungBearbeiten

Die 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 BibliothekBearbeiten

Das Ü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.cBearbeiten

 /* 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.cBearbeiten

/* 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: GTKBearbeiten

unit 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ärungBearbeiten

Da 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: GTKTestBearbeiten

program 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`


AusblickBearbeiten

Das Ü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.

AnmerkungenBearbeiten

  1. In der Programmiersprache "C" wird nicht zwischen Funktionen und Prozeduren unterschieden. In C ist jede Routine eine Funktion.