{{Vorlage:Sprache |Name = C++ |Inlineassembler = Ja |Compiler = [[GNU Compiler Collection | g++]], icc |Plattformen = Alle |Kernel = [[C++-Kernel mit GRUB]] |Homepage = [http://www.open-std.org/jtc1/sc22/wg21/ The C++ Standards Committee] }} '''C++''' wurde 1983 von Bjarne Stroustoup veröffentlicht, um C mit Sprachmitteln der objektorientierten Programmierung zu erweitern. Da C++ somit eine Weiterentwicklung von [[C]] ist, sollte man sich auch den Wikiartikel über [[C]] zu Gemüte führen. == Überblick == Die meisten C++ Features sind ohne zusätzlichen Aufwand sofort verfügbar. Nur für die folgenden Features werden Hilfsfunktionen benötigt, welche der C++ Compiler von Haus aus nicht zur Verfügung stellt: * [[C++#Startup-Code | Startup-Code]] * [[C++#rein virtuelle Funktionen | Rein virtuelle Funktionen]] * [[C++#globale/statische Objekte | globale/statische Objekte]] * [[C++#new/delete | new/delete]] * [[C++#RTTI | RTTI (= Runtime type information)]] * [[C++#Exceptions | Exceptions]] Da jeder Compilerhersteller eine eigene ABI (= Application Binary Interface) zur Verfügung stellt, sind auch die Funktionsnamen sowie auch der Funktionsumfang, von Compiler zu Compiler verschieden. == Startup-Code == Dieser Code wird ausgeführt, bevor die main-Funktion des Programmes/Kernels aufgerufen wird und muss zumindest die Kommandozeilenargumente, mit denen das Programm aufgerufen wurde, an die main-Funktion weiterreichen. == rein virtuelle Funktionen == Wenn eine Klasse eine rein virtuelle Funktion nicht implementiert hat und diese Funktion aufgerufen wird, wird zur Fehlerbehandlung eine Hilfsfunktion aufgerufen, welche im Normalfall das Programm beendet. === g++ === // Lizenz: public domain extern "C" void __cxa_pure_virtual() { // Fehlermeldung anzeigen // Programm/Kernel beenden } == globale/statische Objekte == Da diese vor dem Eintritt in die main-Funktion initialisiert sein sollen, müssen auch Konstruktoren und Destruktoren aufgerufen werden. === g++ === Die Konstruktion der globalen/statischen Objekte ist unter g++ immer gleich. Nur die Destruktion der Selben hängt von der Verwendung des configure-flags --enable-__cxa_atexit und vom Kommandozeilenparameter -fuse-cxa-atexit / -fno-use-cxa-atexit ab. ==== Konstruktoren ==== G++ erstellt eine "ctors*" Sektion, welche ein Array aus Funktionszeigern ist. Diese Funktionszeiger zeigen jeweils auf einen Konstruktor. Man muss nun nur dieses Array durchgehen und alle Funktionen aufrufen und schon hat man alle globalen/statischen Objekte initialisiert. // Lizenz: public domain typedef void (*constructor)(); // Im Linkerskript definiert extern "C" constructor start_ctors; extern "C" constructor end_ctors; extern "C" void initialiseConstructors(); // Ruft die Konstruktoren für globale/statische Objekte auf void initialiseConstructors() { for (constructor* i = &start_ctors;i != &end_ctors;++i) (*i)(); } Diese Funktion muss explizit aufgerufen werden bevor globale/statische Objekte verwendet werden. Dies kann beispielsweise im [[C++#Startup-Code | Startup-Code]] geschehen. ==== Destruktoren (ohne --enable-__cxa_atexit und ohne -fuse-cxa-atexit) ==== G++ erstellt eine "dtors*" Sektion, analog zur "ctors*" Sektion und speicher dort wiederum ein Array aus Funktionszeigern. Diesmal zeigen diese Funktionszeiger aber auf Destruktoren. ==== Destruktoren (mit --enable-__cxa_atexit oder mit -fuse-cxa-atexit) ==== Im Konstruktor eines globalen/statischen Objekts wird die folgende Funktion aufgerufen: int __cxa_atexit(void (* f)(void *), void *p, void *d); f ist ein Funktionszeiger zu einem Destruktor, p der Parameter, der dem Destruktor übergeben werden soll und d ist das sog. "home DSO" (DSO = dynamic shared object). Diese Funktion sollte alle drei Parameter speichern und bei Erfolg 0, andernfalls nicht 0 zurückgeben. Wenn der Kernel/das Programm dann beendet wird, sollte man die Funktion void __cxa_finalize(void *d); mit d = 0 aufrufen, um alle mit __cxa_atexit registrierten Funktionen mit passenden Paramtern aufzurufen. Die registrierten Funktionen müssen in umgekehrter Reihenfolge aufgerugen werden. Außerdem muss man ein Symbol namens __dso_handle bereitstellen. Es wird allerdings nur die Adresse des Symbols verwendet um ein DSO zu identifizieren und um __cxa_atexit aufzurufen. // Lizenz: public domain extern "C" { int __cxa_atexit(void (*f)(void *), void *p, void *d); void __cxa_finalize(void *d); }; void *__dso_handle; struct object { void (*f)(void*); void *p; void *d; } object[32] = {0}; unsigned int iObject = 0; int __cxa_atexit(void (*f)(void *), void *p, void *d) { if (iObject >= 32) return -1; object[iObject].f = f; object[iObject].p = p; object[iObject].d = d; ++iObject; return 0; } /* NOTE: Zerstört momentan unabhängig von d alle Objekte */ void __cxa_finalize(void *d) { unsigned int i = iObject; for (; i > 0; --i) { --iObject; object[iObject].f(object[iObject].p); } } == new/delete == Um dynamisch Objekte mit new erstellen bzw. mit delete zerstören zu können muss man die vier Operatoren *new *new[] *delete *delete[] implementieren. Eine einfache Implementation ist z.B.: void *operator new( size_t size ) { return kcalloc( size ); } void *operator new[]( size_t size ) { return kcalloc( size ); } void operator delete( void *ojb ) { kfree( obj ); } void operator delete[]( void *obj ) { kfree( obj ); } == RTTI == RTTI (Runtime Type Information) wird am besten innerhalb des Kernels komplett deaktiviert. Mit g++ geschieht dies mittels des Kommandozeilenparameter ''-fno-rtti''. Ohne RTTI muss man aber auch auf ''dynamic_cast'' und ''typeid'' verzichten. == Exceptions == Exceptions sollten auch am besten innerhalb des Kernels deaktiviert werden. Mit g++ können Exceptions über den Kommandozeilenparameter ''-fno-exceptions'' deaktiviert werden. == sonstige Probleme == === C++ Funktionen aus C/Assembler nutzen === Um C++ Funktionen von Sourcecode, der in C oder Assembler geschrieben ist, aufrufen zu können, muss das ''name mangling'' - dabei werden die Funktionsnamen nicht direkt als Symbolnamen genommen, sondern Parameter- und Rückgabetypen und Informationen zum Namespace mit in den Symbolnamen eingebaut - deaktivieren für diese Funktion. Dies geschieht dadurch, dass die betreffende Funktion innerhalb des C++-Sourcecodes ''extern "C"'' deklariert wird, wie in folgendem Beispiel: extern "C" void foobar(int i, char* s); Anschließend kann die Funktion in C++ ganz normal definiert werden und schließlich aus dem C/Assembler-Sourcecode heraus wie sonst üblich aufgerufen werden. === TODO === Das Problem hier ist, dass C++ im Gegensatz zu C keine TODO unterstützt, beispielsweise wird der Code struct { uint16_t limit; void* pointer; } __attribute__((packed)) gdtp = { .limit = GDT_ENTRIES * 8 - 1, .pointer = gdt, }; beim Kompilieren eine Fehlermeldung namens ''expected primary-expression before ‘.’ token'' ausgeben. Man kann allerdings den gleichen Effekt entweder durch Weglassen des Feldnamens erreichen (dann muss die Initialisierung in der Reihenfolge wie die Definition des Strukturtyps sein): struct { uint16_t limit; void* pointer; } __attribute__((packed)) gdtp = { GDT_ENTRIES * 8 - 1, gdt, }; Die andere Möglichkeit ist einen Konstruktor für die Struktur zu definieren - dafür muss aber die Struktur benannt werden: struct gdt_register { uint16_t limit; void* pointer; gdt_register(uint16_t _limit, void* _pointer) : limit(_limit), pointer(_pointer){} } __attribute__((packed)); gdt_register gdtp(GDT_ENTRIES * 8 - 1, gdt); == Siehe auch == [[C++-Kernel mit GRUB]] == Links == * [http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2006/n2135.pdf Draft des C++ Standards] * [http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1836.pdf Draft des TR1 (=Technical Report 1)] * [http://www.codesourcery.com/cxx-abi/abi.html g++ ABI] (Nicht nur die ABI für Itanium, sondern auch für x86 und x86-64) * [http://www.codesourcery.com/cxx-abi/abi-eh.html g++ ABI: Exception Handling] (Nicht nur die ABI für Itanium, sondern auch für x86 und x86-64) * [http://www.osdev.org/wiki/C_PlusPlus OSDev.org wiki] [[Kategorie:Programmiersprache]]