Ein Durchschlupf fuer die Objektwelt Wie Smalltalk-Oberflaechen auf C++-Anwendungen passen

30.06.1995

Vermutlich stehen nicht wenige Programmierer und Systemintegratoren vor der Frage, wie sich das Zusammenspiel von C++-Applikationen und Smalltalk-Bedienoberflaechen umsetzen laesst. Bernd Scharlemann* stellt zwei Kopplungsalternativen vor, eine Ein- und eine Mehrprozessloesung. Vor- und Nachteile dieser Moeglichkeiten werden angerissen.

Smalltalk ist unbestritten die einfachere der beiden objektorientierten Sprachen. Alle Datentypen sind Objekte. Den Unterschied zwischen Objekten und elementaren Datentypen kennt Smalltalk im Gegensatz zu C++ nicht. Durch die integrierte Entwicklungsumgebung von Smalltalk, in der geaenderte Quellprogramme sofort ausfuehrbar sind, ist der Entwicklungszyklus sehr kurz. Eine automatische Speicherverwaltung mit Hilfe der Funktionen "Memory Management" und "Garbage Collection" etwa befreit Programmierer von der Notwendigkeit, sich um Details des Speicher-Managements zu kuemmern. Daher eignet sich Smalltalk besonders gut fuer grosse und komplexe Systeme, zu denen auch komfortable Benutzer-Schnittstellen zu rechnen sind.

Die hier vorgestellte C++-Smalltalk-Schnittstelle soll es ermoeglichen, eine Grafikmaschine aus "Visual Works 2.0" von Parkplace sowie zusaetzliche Eigenentwicklungen, etwa ein Widget fuer animierte Objektgrafiken, auch fuer die C++-Welt nutzbar zu machen. Zu den Services dieser Schnittstelle gehoeren ein Namens- und Erzeugungsservice.

Die Schnittstelle ist wegen der Unabhaengigkeit von hinzugekauften Klassenbibliotheken portabel und universell einsetzbar fuer ein Nachrichten-Interface zwischen Smalltalk- und C++-Objekten. Sie dient allerdings nicht zum Austausch von Objekten, ist kein Object Request Broker. Es wird vorausgesetzt, dass die beteiligten Klassen auf beiden Seiten bekannt sind. Lediglich die beteiligten Instanzen sind variabel.

Waehrend mit Hilfe von Visual Works die Programmierung von Standard-Widgets weitgehend automatisiert ist, wird die Schnittstellen-Schicht direkt in Smalltalk programmiert. Ist die Anwendung kein Smalltalk-Programm, werden die Aufrufe von Anwendungsservices als Nachrichten ueber die Smalltalk/C++- Schnittstelle implementiert wie auch Aenderungen in der Anwendung.

Gute Gruende, C++ zur Sprache der Wahl zu machen, sind unter anderem:

- die Nutzung vorhandener C++-Software,

- die Einbindung anderer Programmiersprachen ueber C++,

- die Hardwarenaehe von C sowie

- die Laufzeiteffizienz von C++.

Die Entwicklung der Smalltalk-C++-Schnittstelle muss vor allem folgende Probleme loesen:

- Wie koennen Objekte auf der anderen Seite adressiert werden?

- Wie laesst sich ein typisiertes System wie Smalltalk mit einem streng typisierten wie C++ verbinden?

- Wie sollen die Unterschiede in den elementaren Datentypen behandelt werden, und soll das Interface als Ein- oder Mehr- Prozess-Loesung realisiert werden?

Smalltalk-Objekt-Adressen sind wegen des automatischen Memory Managements beweglich. Dies bedeutet, dass eine Smalltalk-Objekt- Adresse in einer C++-Umgebung fluechtiger Natur und somit als zuverlaessige Objektreferenz nicht verwendbar ist.

C++-Objektadressen sind jedoch ueber die gesamte Lebensdauer der Objekte konstant und koennten von Smalltalk aus als Objektreferenzen genutzt werden. Wird jedoch ein C++-Objekt ohne Wissen der Smalltalk-Seite geloescht, fuehrt die naechste Nachricht an dieses Objekt zum Zugriff auf eine ungueltige Speicheradresse und damit zum Absturz des Prozesses.

Das Problem laesst sich mit der Einfuehrung von Name-Services beseitigen (siehe Abbildung 1). Anstatt mit Hilfe von Adressen werden die Objekte ueber Namen (IDs oder Keys) referenziert. Dafuer muss sich jedes ueber den Name-Server ansprechbare Objekt beim Name- Server anmelden. Spaetestens zum Zeitpunkt seiner Freigabe beziehungsweise Zerstoerung muss sich ein eingetragenes Objekt wieder austragen. Darueber hinaus muss der Name-Server in der Smalltalk-Welt alle Objekte, die von C++ aus angesprochen werden koennen, kennen; ein Name-Server in der C++-Welt alle Smalltalk- Objekte.

Mit dem Name-Server auf Smalltalk-Seite bleibt jedoch immer ein Objekt uebrig, dessen Adresse in einer Ein-Prozess-Loesung von C++ aus benoetigt wird. Dieses Objekt und damit seine Adresse wird in einem Slot der virtuellen Maschine von Smalltalk eingetragen. Vor jedem Smalltalk-Aufruf holt sich die C++-Seite die aktuelle Adresse des Smalltalk-Name-Servers frisch aus dem Slot. Diese aktuelle Adresse bleibt jeweils solange gueltig, wie kein Smalltalk-Code ablaeuft.

Die strenge Typpruefung in C++, die im allgemeinen sehr hilfreich ist, weil sie etwa Fehler bei der Datenversorgung von Aufruf- Schnittstellen schon zur Laufzeit des Compilers aufdecken kann, macht es schwierig, zusammengestellte Daten, heterogen in Anzahl und Typ, ueber weite Wege zu transportieren. Daher benoetigt man einen Mechanismus, bei dem nur Sender und Empfaenger den Inhalt eines Datenpaketes per Absprache kennen muessen. Alle Transportstationen dazwischen reichen das Datenpaket lediglich weiter.

Klassische Loesungen in der C-Welt sind mit Unions und/oder Void- Pointern implementiert. Diese Loesung ist in diesem Fall nicht flexibel genug. So waere es beispielsweise nicht machbar, implizit Datentypwandlungen vorzunehmen, wie sie bei einer Smalltalk/C++- Kopplung notwendig sind.

Eine Moeglichkeit, das Problem zu loesen, besteht in einem Value- Holder, einer C++-Klasse, die die Smalltalk-Faehigkeit, in einem Array eine Sammlung heterogener Daten zu halten, nachstellt (siehe Abbildung 2). Die Nachrichten werden beim Senden in dem Value- Holder verpackt. Die Zwischenstationen des Transportwegs kennen nur den Datentyp des Value-Holders. Erst der Empfaenger der Nachricht greift wieder auf den Inhalt zu.

Die Anzahl der Dimensionen eines Value-Holders ist variabel. Er kann als Skalar, als ein- oder mehrdimensionales Array eingesetzt werden. Dabei ist die Array-Laenge fuer jedes einzelne eindimensionale Teil-Array ebenfalls variabel. Jeder Eintrag in einem Value-Holder hat einen individuellen Datentyp. Der Empfaenger kann vor dem Zugriff auf die im Value-Holder abgelegten Daten die Laengen der Arrays und die Datentypen erfragen.

Zur Zeit lassen sich mit dem realisierten Value-Holder die folgenden Datentypen unterstuezen

- nil (enum mit nur einem Wert),

- bool (enum mit den Werten 0 und 1),

- char (signed/unsigned),

- integer (2/4 Byte, signed/un-

signed),

- real (float/double),

- text (char pointer, intern abgelegt als string),

- point (class mit x und y),

- object reference (class mit Instanzen- und Klassenname),

- exception (class mit Code und Text),

- void pointer (nur innerhalb von C++ sinnvoll) sowie

- array (heterogen, alle Elemente sind selbst wieder Value-Holder; ein Array darf alle aufgefuehrten Datentypen enthalten).

Zu den Moeglichkeiten, die Kopplung von Smalltalk- und C++- Programmen zu realisieren, gehoert die Zusammenfassung zu einem Prozess oder alternativ die Aufteilung auf mehrere Prozesse in einer Netzwerkumgebung.

Bei der Ein-Prozess-Loesung bietet sich die Moeglichkeit, C++-Code in eine Smalltalk-Umgebung einzubinden und eine direkte C++-Aufruf- Schnittstelle zu verwenden. Dabei bildet ein Smalltalk-Prozess das Rahmenprogramm. Die C++-Anteile sind eine Sammlung von C- Funktionsaufrufen, die als Dynamic-Link-Libraries

(DLLs) zur Verfuegung stehen (siehe Abbildung 3).

Die Einbindung von C++ in Smalltalk geschieht in Visual Works ueber einen Teil des Advanced-Programming-Kits mit dem Namen Objectkit/Smalltalk C++ Programming (vormals CPOK). Will ein Smalltalk-Objekt eine C-Funktion aufrufen, so muss diese Funktion in einer speziellen Schnittstellen-Klasse (Subclass of External Interface) als Instanzenmethode implementiert sein. Hier findet auch die Umsetzung von Smalltalk- in C++-Datentypen statt.

Direkt koppeln oder Sockets benutzen?

Der zugehoerige Objektcode wird zur Laufzeit als DLL herangezogen. Die C++-Seite kommuniziert mit der Smalltalk-Seite ueber einen Satz von Runtime-Library-Functions, die in die virtuelle Maschine von Smalltalk integriert sind. Vorteil dieser Moeglichkeit ist in erster Linie der geringe Overhead. Nachteilig hingegen wirkt sich die direkte Kopplung auf das Debugging und auf die Robustheit des Programms aus.

So endet die Reichweite des integrierten Smalltalk-Debuggers mit dem Aufruf einer C++-Funktion. Tritt ein Fehler in einer aus C++ aufgerufenen Smalltalk-Methode auf, wird lediglich ein Fehler im C++-Aufruf gemeldet. Es bleibt nur noch die muehsame Moeglichkeit, sich mit Breakpoints und Einzelschritten an die Smalltalk- Fehlerstelle heranzutasten.

Ausserdem stehen alle Smalltalk-Prozesse still, solange der C++- Code durchlaufen wird. Zudem fuehrt ein Laufzeitfehler etwa der Kategorie segmentation fault zum Absturz der virtuellen Smalltalk- Maschine. Wird dabei ein Speicherabzug (core dump) generiert, ist dieser riesig und im allgemeinen nicht auswertbar.

Die zweite Moeglichkeit, C++ und Smalltalk gemeinsam in einem System einzusetzen, ist die Aufteilung der C++- und Smalltalk- Anteile in eigenstaendige Prozesse. Der Austausch von Informationen geschieht dann ueber Nachrichtentelegramme, die zwischen den Prozessen ausgetauscht werden.

Als Basis fuer eine netzwerkfaehige Interprozess-Kommunikation dient eine Socket-Schnittstelle: Ein Prozess wartet als Server darauf, dass sich ein Client fuer einen Verbindungsaufbau anmeldet. Nach dem Aufbau der Verbindung sind beide Kommunikationspartner gleichberechtigt. Die benutzte Netzwerkverbindung verwendet als Transportschicht das Internet-Protokoll TCP/IP und als Schnittstelle zu dieser Transportschicht BSD- oder Win-Sockets. Eine Nachricht, bestehend aus Empfaenger, Nachrichtenname und Parametern, wird von der Senderseite in ein sprachneutrales binaeres Nachrichtentelegramm verpackt, via Sockets an die Empfaengerseite uebertragen und dort wieder ausgepackt (siehe Abbildung 4).

Diese Netzwerkloesung hat mehrere Vorteile:

- die Debugging-Moeglichkeiten in Smalltalk sind nahezu uneingeschraenkt,

- ein C++-Prozess kann im C++-Debugger ueberprueft werden,

- C++ und Smalltalk koennen asynchron parallel und zudem auf verschiedenen Rechnern ablaufen,

- Smalltalk-Prozesse sind vor C++-Abstuerzen sicher sowie

- fuer die Kopplung gleichsprachiger Prozesse geeignet und

- ausbaufaehig von einer Zwei- zu einer Mehr-Prozess-Loesung.

Nachteil dieser Loesung mag der zusaetzliche Overhead sein, der durch das Ein- und Auspacken der Nachrichten und den eigentlichen Netzwerktransport entsteht. Zusaetzlicher Aufwand faellt aber auch an, wenn zwei Prozesse synchron laufen sollen. Ausserdem wird die Portabilitaet eingeschraenkt, da nicht alle Betriebssysteme eine vollstaendige Unterstuetzung von BSD-Sockets bieten.

*Bernd Scharlemann ist beim Debis Systemhaus, Hamburg, beschaeftigt. Zur Implementierung siehe Conference Proceeding der Entwicklerkonferenz "Software Devcon 1995."