Die hier vorgestellte Library-Klassen und Methoden ("C-Funktionen") ist geeignet als Basis für eine Programmierung auch in C, oder in C++, bei denen grundlegende Dinge an die Java-Denkweise angelegt sind:
Dynamisches Binden (virtuelle Methoden) auch in C
Object als Basisklasse aller Klassen, um gemeinsame Eigenschaften aller Instanzen auszudrücken.
Speicherverwaltung für dynamische Speicheranforderungen echtzeitgeeignet (Pool gleichgroßer Speicherblöcke)
Stringverarbeitung (String, StringBuffer) wie in Java, unter Echtzeitbedingungen
Fehlerbehandlung ähnlich in Java und C++ mit try, catch und throw
Diese Leistungen sind nutzbar, obwohl ansonsten 'normal' in C programmiert wird. In C++ sind solche Dinge wie dynamisches Binden und Klassen selbstverständlich, hier ist die Besonderheit 'nur' diese, dass die C_RuntimeJavalike - Klassen Java-ähnlich sind.
In C können drei Nutzungsvarianten unterschieden werden:
Nur einfache Programme, klassenorientiert, aber keine Interfaces und kein dynamisches Binden, kein GarbageCollector im RuntimeHeap: In diesem Fall ist eine Referenz auf eine Klasse ein einfacher Zeiger.
Nutzung des dynamischen Bindens in C (Interfaces): In diesem Fall müssen alle Referenzen mit Zusatzinformationen ausgestattet werden. Daher passt eine Referenz nicht in ein Adressregister, sondern benötigt 2 mal 4 Byte (32-bit-System) oder auch nur 2 mal 2 Byte (16-bit).
Diese Unterschiede können in der einen oder anderen Anwendung von Bedeutung sein: Was nicht benötigt wird, soll auch keinen unnötigen Overhead erzeugen.
In C++ entstehen mindestens ebenfalls zwei dieser Nutzungsvarianten: mit oder ohne Nutzung des Garbage-Collectors:
Wird der Garbage Collector im RuntimeHeap nicht benutzt, dann sind auch keine extra Referenzklassen notwendig, eine Referenz ist wie in C++ ein einfacher Zeiger. Dynamisches Binden ist der in C++ eigenen Weise realisiert.
Wird der Garbage Collector benutzt, oder ist ein gewisser Mix mit C-Funktionen mit dynamischem Binden nach der C-Variante gewünscht, dann muss auch hier die Referenz aus zwei Teilworten bestehen.
Es ist sinnvoll, in C++ gemischt mit C-Strukturen zu arbeiten. Und zwar wenn innerhalb einer objektorientierten C++-Umgebung Datenstrukturen so aufgebaut werden sollen, dass ihre binäre Übergabe an ein anderes außerhalb befindliches Programmteil möglich ist. Bei dem Programmteil außerhalb kann es sich um ein Programm in C++ mit einem anderen Compiler, in C oder in einer anderen Programmiersprache wie zum Beispiel Java handeln. Die binären Daten der Klasseninstanzen von C++ enthalten Zeiger auf virtuelle Tabellen, insgesamt ist vom C++-Standard nicht garantiert, das diese Daten bei gleichen Quellen binärkompatibel sind. Compiler haben hier Implementierungsfreiheiten. In C wird dagegen davon ausgegangen, dass Binärkompatibilität von Datenstrukturen garantiert werden kann, wenn bestimmte Bedingungen eingehalten werden. Dazu gehört die Nichtverwendung des Typs int und von enum-Typen in Datenstrukturen. Diese sind plattformabhängig. Für Integerdaten mit bestimmter Datenbreite muss es für jede Plattform eine eigene Definition geben, die im Anwender-Quelltext dann üblicherweise mit int32, int16 oder ähnlich bezeichnet ist.
In C++ kann man solche C-Strukturen verwenden. Sie werden in der Literatur als POD (Plain Old Data) bezeichnet.
Interessant ist es, für solche PODs auch Reflections zu benutzen und bestimmte Methoden von Object dynamisch gebunden zu nutzen, gedacht ist insbesondere an toString(), um eine einfache manuell lesbare Information über den Inhalt zu bekommen. An dieser Stelle kann also in einer ansonsten reinen C++-Umgebung auch das C-like-Dynamische Binden eingesetzt werden. Das kann unabhängig von der Entscheidung der Nutzung von Referenzen (Garbage Collection) erfolgen. Für den Aufruf virtueller Methoden einer C-like-Klasse (nicht eines C-like-Interfaces) sind keine Referenzen notwendig.
In dem grundlegendem Headerfile Object_Jc.h wird ein Headerfile CRuntimeJavalike_SysConventions.h
includiert. Dieser Headerfile muss im Userspace bereitgestellt werden. Darin werden die System-Konventionen (= Nutzungsvarianten) benannt.
In diesem Headerfile muss für die jeweilige Implementierungsplattform folgende typedefs enthalten sein. Die typischen Werte sind für einen 32-bit-Compiler angegeben.
typdef | Typ. Wert | Bedeutung |
---|---|---|
boolean | int, bool | Datentyp für boolean |
bool | int | Datentyp für boolean. In C++ ist bool bereits ein Schlüsselwort, dann nicht zu definieren. |
int8 | char | Integer-Typ in einem Byte, entspricht in Java byte |
int16 | short | Integer-Typ in zwei Bytes, entspricht in Java short |
int32 | int | Integer-Typ in vier Bytes, entspricht in Java int |
int64 | double int | Integer-Typ in acht Bytes, entspricht in Java long. Ist die Definition nicht möglich (16-bit-System), dann kann long angegeben werden und stattdessen bei int64 auch mit 32 bit gearbeitet werden. In der CRuntimeJavalike selbst gibt es keine Notwendigkeit der unbedingten Nutzung von 64 bit. |
In diesem Headerfile muss für die jeweilige Implementierungsplattform folgende defines enthalten sein.
Definition | typ. Wert | Bedeutung |
---|---|---|
true | ~0 | In C++ ist bool bereits ein Schlüsselwort, dann nicht zu definieren. ~0 hat sich als günstiger erwiesen als 1. Dabei sind alle Bits gesetzt. |
false | 0 | In C++ ist bool bereits ein Schlüsselwort, dann nicht zu definieren. |
null | ((void*)(0)) | In C++ mit 0 zu definieren, in C mit dem angegebenem Wert. Damit wird ein null-Zeigerwert definiert. |
Weiterhin müssen folgende Definitionen vorhanden sein, die die Art der Verwendung der CRuntimeJavalike definieren.
Definition | typ. Wert | Bedeutung |
---|---|---|
__CPLUSPLUS_Jcpp |
Ist dieses Define gesetzt, dann werden, C++-Compilierung vorausgesetzt, für alle Klassen auch die C++-class-Variante compiliert. Dann werden auch die Defines, die mit _Jcpp enden, für die C++-Variante definiert. Insbesondere werden virtuelle Methoden, die über Ist dieses define nicht gesetzt, dann werden auch bei C++-Compilierung nur die C-Varianten bereitgestellt. |
|
DONOTUSE_GC_Jc | Ist dieses Define gesetzt, dann wird der Garbage-Collector nicht unterstützt. Dieses Define ist für die Anwenderprogrammierung wichtig. Der Anwender muss sich um das Löschen angelegter Instanzen selbst kümmern, wenn dieses Define gesetzt ist. | |
ObjectRefValues_c | int32 | Dieser Typ wird benutzt für das abspeichern der Zusatzinformationen in erweiterte Referenzen. Er sollte die gleiche Byteanzahl wie ein pointer besitzen. |
mIdxVtbl_Object_JcRef | 0x00FF |
Ist dieses Define gesetzt, dann werden erweiterte Referenzen für die internen Quellen von CRuntimeJavalike und für die Makros für Referenzen aus Das Define beschreibt die Anzahl der verwendeten Bits in erweiterten Referenzen für den Index der Lage der virtuellen Tabelle des Interfaces innerhalb der gesamten virtuellen Tabelle in den Reflections der Klasse. Mit dem angegebenem Wert werden max. 255 virtuelle Methoden pro Klasse unterstützt. |
mBackRef_Object_JcRef | 0xFF |
Ist dieses Define nicht gesetzt, dann wird der Garbage-Collector nicht unterstützt. Dann muss aber das Define DONOTUSE_GC_Jc gesetzt sein. Über dieses Define wird gesteuert, ob bei erweiterten Referenzen Rechenzeit für einen Rückwärtsreferenz-Eintrag auftritt.Die erweiterten Referenzen werden genutzt, wenn C-like-Interfaces (virtuelle Tabellen) notwendig sind. Ist dieses Define gesetzt, dann wird der Garbage-Collector prinzipiell unterstützt. Dann müssen alle folgenden Defines für GargabeCollection definiert sein. Mit diesem Define wird festgelegt, wieviel Bits für den Index der Rückwärtsreferenz in einer Referenz verwendet werden. |
kBitBackRef_Object_JcRef | 8 | Definition für GarbageCollection, notwendig wenn mBackRef_Object_JcRef gesetzt. Lage der Bits für den Index der Rückwärtsreferenz in einer erweiterten Referenz. |
__TRYCPP_Jc |
Ist dieses Define gesetzt, dann wird die C++-try-Variante für die TRY- und CATCH-Makros verwendet. Das geht nur, wenn C++-Compilierung insgesamt verwendet wird. Ist dieses Define nicht gesetzt, dann wird die longjmp-Variante für TRY- und CATCH-Makros verwendet. Das ist sowohl in C als auch C++ möglich. Beim Einsatz dieser Variante in C++ werden aber Destruktoren in zwischenliegenden Methodenebenen, die Fehler nicht abfangen, nicht abgearbeitet. |
|
METHOD_C | extern "C" |
Dieses Define muss bei C++-Compilierung mit extern "C" gesetzt sein, wenn die C-Quellen für CRuntimeJavalike mit einem C-Compiler übersetzt werden. Das Define kann bei C++-Compilierung mit extern "C" gesetzt sein, wenn die C-Quellen für CRuntimeJavalike mit einem C++-Compiler übersetzt werden, in diesem Fall werden alle Labels von Methoden nach dem C-Style gebildet. Dieses Define kann bei C++-Compilierung der C-Quellen für CRuntimeJavalike leer gesetzt sein, dann werden die Labels der Methoden nach dem C++-Style gebildet. Die Labels sind für den Linker-Lauf ausschlaggebend. Bei widersprüchlicher Verwendung dieses Defines gibt es erkennbare Linkerfehler. Dieses Define muss bei C-Compilierung leer gesetzt sein. |
mIsConstant_String_Jc | 0xF000 | Kennzeichnende Bits, wenn in einem String_Jc ein konstantes Stringliteral gezeigert ist. Das höchstwertigste Bit dieser Maske muss korrespondieren zum höchstwertigsten Bit von mBackRef_Object_JcRef geschoben um kBitBackRef_Object_JcRef , da der Typ String_Jc gleichzeitig auch eine Referenz auf einen nichtkonstanten String darstellen kann, die selbigen Bits dann für mBackRef_Object_JcRef benutzt werden. |
mLength_String_Jc | 0x0FFF | In diesen Bits ist in einem String_Jc die Anzahl der Zeichen eines konstanten Stringliterals gespeichert. Die Anzahl der Bits beschränkt die maximale Länge einer konstanten Zeichenkette. In einem 32-bit-System können alle defines m... , die in dieser Tabelle genannt sind, auch um entsprechende Bits erweitert werden. Die Beispiele zeigen sinnvolle Werte für ein 16-bit-System. |
In den Quellen der CRuntimeJavalike befindet sich ein Muster dieser Datei, die für C-Verwendung mit Garbage-Collection zugeschnitten ist. Mehrere Möglichkeiten sind jeweils kommentiert. Der Anwender sollte bei abweichenden Definitionen diese Datei an dieser Stelle herausnehmen, modifizieren und in einem anderen Verzeichnis speichern. Das andere Verzeichnis muss im Include-Path erreichbar sein. Es sollten keine Anwender-Modifikationen innerhalb der CRuntimeJavalike-Originalquellen bezüglich Anwendersystem erfolgen, da es sonst Verwechslungen geben kann.
In der C-like-Schreibweise sollen Aufrufe von Methoden von C-like-Klassen mit
methodName_Type(reference, paramter ...)
geschrieben werden. Wenn es sich um virtuelle Methoden handelt, dann sollen die Methoden im Headerfile als define wie nachfolgend beschrieben ausgeführt sein. In Abhängigkeit davon, ob die Implementierung des Aufrufes in C oder C++ als echte Klassenmethode erfolgt, wird entweder eine Befehlsfolge für das dynamische Binden in C erzeugt, oder die C++-Aufrufschreibweise
reference->methodName(paramter ...).
Grundsätzlich gibt es zwei Varianten:
a) Auch in der C++-Variante soll die C-like-Aufrufmethode von virtuellen Methoden verwendet werden, das ist dann der Fall, wenn es für die betreffende Klasse keine C++-Form gibt, sondern nur die C- (Plain Old Data-) Struktur mit zugehörigen Methoden.
Dazu kommt noch die Unterscheidung des Aufrufes von Methoden aus Klassen und aus Interfaces, in C werden diese unterschiedlich realisiert. Damit ergeben sich folgende vier Makros für die Definition von virtuellen Methoden:
im Headerfile die virtuellen C-like gibt es damit folgende Varianten:
Makro | Anwendung |
---|---|
VCALL_CLASS_Jc | Eine Klasse ist als C-Stuktur vorhanden, das dynamische Binden soll C-like realisiert werden. Das kann auch bei C++ vorkommen, wenn für Plain Old Data bestimmte Methoden überladen aufgerufen werden sollen (Object_c.toString()). |
VCALL_IFC_Jc | Eine Interface ist C-like vorhanden. Das Interface besteht in diesem Fall aus einer Datenstruktur vom Typ Object_c und einer virtuellen Tabelle. Das kann zwar prinzipiell auch in C++ vorkommen, wenn entgegen den C++-Verfahrensweisen Plain Old Data mit Interfaces versehen werden, ist aber eher nicht zu erwarten. |
VCALL_CLASS_Jcpp | Eine Klasse ist entweder als C-Struktur oder als C++-Klasse eingehüllt vorhanden. In C++ soll auch nur die C++-Klasse verwendet. |
VCALL_IFC_Jcpp | Ein Interface ist entweder als C-Struktur oder als C++-Klasse vorhanden. In C++ soll auch nur die C++-Klasse verwendet |
Die ersten beiden Makros realisieren in jedem Fall das dynamische Binden wie es in C vorgesehen ist. Für das Makro VCALL_CLASS_Jc ist nur der Zeiger auf die Instanz notwendig, es ist keine Referenz mit Index auf die virtuelle Tabelle in der Klasse notwendig, da die gesamte Tabelle ab 0 gilt. Folglich funktioniert dies auch, wenn man keine Referenzen mit virtuellem Index benutzt sondern Referenzen einfache Zeiger sind. Demzufolge ist dieses Makro plattformunabhängig in der Object_c.h definiert.
Das Makro VCALL_IFC_Jc benötigt dagegen eine Referenz bestehend aus zwei Speicherworten, funktioniert also nicht in einer Umgebung, wo mit solchen Referenzen nicht gearbeitet wird. Es ist in den plattformabhängigen Headerdateien definiert, wo es verwendet wird.
Die Makros für _Jcpp sind unterschiedlich für eine reine C-Plattform und eine C++-Plattform definiert. Bei der C-Plattform sind sie identisch mit den jeweiligen _Jc-Makros. Für eine C++-Plattform wird das dynamische Binden aus C++ benutzt. Damit liefern die Implementierungen aus der selben Quelle unterschiedliche Wirkungsweisen. Verwendet ist das insbesondere für Thread_Jc und Runnable_Jc beziehungsweise den entsprechenden Gegenstücken Thread_Jcpp und Runnable_Jcpp.