
Anstelle des Vorworts
Vielleicht sollte mit diesem Bild eine Geschichte über
Boost ,
Loki , Independent und auch Implementierungen der mit Compilern gelieferten Standard-C ++ - Bibliothek beginnen.
Ja, ja, und wenn Sie dachten, dass die Entwickler der Standardbibliothek für dasselbe g ++, clang, Visual Studio oder, Gott sei Dank, C ++ Builder (früher Borland, aber der aktuelle Embarcadero) Gurus sind, die keine Krücken herstellen, brechen sie nicht den Standard für ihren Compiler Wenn Sie keine Fahrräder schreiben, verwenden Sie die Standard-C ++ - Bibliothek höchstwahrscheinlich nicht so aktiv, wie Sie gedacht haben.
Der Artikel ist als Geschichte geschrieben und enthält viel „Wasser“ und Abschweifungen, aber ich hoffe, dass meine Erfahrung und der daraus resultierende Code für diejenigen nützlich sind, die bei der Entwicklung in C ++ mit ähnlichen Problemen konfrontiert waren, insbesondere bei älteren Compilern. Link zu GitHub mit dem Ergebnis für heute für ungeduldige und Nichtleser:
https://github.com/oktonion/stdex (Commits und konstruktive Kritik sind willkommen)
Und jetzt das Wichtigste zuerst.
Inhaltsverzeichnis
Einführung
Kapitel 1. Viam Supervadet VadensKapitel 2. #ifndef __CPP11_SUPPORT__ #define __COMPILER_SPECIFIC_BUILT_IN_AND_MACRO_HELL__ #endifKapitel 3. Finden der perfekten nullptr-ImplementierungKapitel 4. C ++ Template Magic....
4.1 Wir fangen klein an....
4.2 Über wie viele wundersame Fehler das Protokoll für uns kompiliert....
4.3 Zeiger und alles in allem....
4.4 Was wird sonst noch für die Vorlagenbibliothek benötigt?Kapitel 5
...
Eintrag
Es war 2017, C ++ 11 ist seit langem in einen neuen Strom neuer und relativ neuer Compiler eingebrochen, der standardisierte Arbeit mit Streams, Mutexen, erweiterte Vorlagenprogrammierung und Standardisierungsansätze bringt. Schließlich gibt es
im Standard „große“
lange lange Typen Die weit verbreitete Notwendigkeit, Typen für den Compiler mithilfe von
auto anzuzeigen (auf Wiedersehen
std :: map <Typ, Typ> :: const_iterator it = ... - Sie verstehen mich), wurde
beseitigt , und die Kombination dieser Funktion mit der neuen
für jede ist zu einer der häufigsten geworden verwendete Loop-Iterator-Implementierungen. Schließlich konnten wir (Entwickler) dem Benutzer (Entwickler) menschlich mitteilen, warum der Code nicht mit
static_assert sowie
enable_if erfasst wird , das nun die erforderlichen Überladungen wie von Zauberhand auswählt.
Auf dem Hof war 2017! Bereits C ++ 17 wurde aktiv in GCC, Clang, Visual Studio eingeführt, überall gab es
Decltype (seit C ++ 11),
Constexpr (seit C ++ 11, aber deutlich verbessert), die Module waren fast unterwegs, es gab eine gute Zeit. Ich war auf der Arbeit und habe mit einiger Missbilligung den nächsten internen Compiler-Fehler in meinem Borland C ++ Builder 6.0 sowie viele Build-Fehler mit der nächsten Version der Boost-Bibliothek untersucht. Ich denke, jetzt verstehst du, woher dieses Verlangen nach Fahrradbau kam. Wir haben Borland C ++ Builder 6.0 und Visual Studio 2010 für Windows, g ++ Version 4.4.2 oder niedriger für
QNX und einige Unix-Systeme verwendet. Wir wurden von MacOS verschont, was zweifellos ein Plus war. Keine anderen Compiler (einschließlich C ++ 11) konnten aus Gründen, die wir außerhalb dieses Artikels belassen, überhaupt in Betracht gezogen werden.
"Und was könnte dort so kompliziert sein" - ein Gedanke schlich sich in meine erschöpften Versuche ein, unter dem guten alten Erbauerhirn Auftrieb zu geben. "Alles was ich brauche ist
type_traits ,
thread ,
mutex , vielleicht
chrono ,
nullptr wäre nett." Ich überlegte und machte mich an die Arbeit.
Kapitel 1. Viam Supervadet Vadens
Es war notwendig, von wo und von wo aus zu beginnen - natürlich hatte ich eine Reihe von Header-Dateien und Quellcodes über Projekte verteilt, mit Implementierungen ähnlicher oder identischer Funktionalität aus der Standard-C ++ 11-Standardbibliothek meiner Entwicklung sowie ehrlich ausgeliehen oder verarbeitet aus den Codes davon gleiche gcc und Boost. Durch die Kombination all dessen bekam ich ein Durcheinander von Funktionen, Klassen und Makros, die sich in eine elegante und schlanke Standardbibliothek verwandeln sollten. Nachdem ich den Arbeitsaufwand geschätzt hatte, entschied ich mich sofort, die Implementierung von allem und jedem aufzugeben und mich auf die Entwicklung eines „Add-Ons“ über die Standard-C ++ 98-Bibliothek zu beschränken, die mit dem Compiler geliefert wird.
In der ersten Version gab es keine besondere Einhaltung des Standards, hauptsächlich angewandte Probleme wurden gelöst. Zum Beispiel sah
nullptr folgendermaßen aus:
#define nullptr 0
static_assert wurde auch einfach gelöst:
#define STATIC_ASSERT(expr) typedef int test##__LINE__##[expr ? 1 : -1];
std :: to_string wurde über
std :: stringstream implementiert , das in Implementierungen ohne die
sstream- Headerdatei durch
std :: strstream ersetzt wurde, und all dies wurde sofort in den
Namespace std verschoben :
#ifndef NO_STD_SSTREAM_HEADER #include <sstream> #else #include <strstream> namespace std {typedef std::strstream stringstream;} #endif namespace std { template<class T> string to_string(const T &t) { stringstream ss; ss << t; return ss.str(); } }
Es gab auch „Tricks“, die nicht im Standard enthalten waren, aber dennoch in der täglichen Arbeit nützlich waren, wie zum Beispiel die
Ewigkeit oder die
Anzahl der Makros :
#define forever for(;;)
countof wurde dann in eine C ++ - Option umgewandelt:
template <typename T, std::size_t N> char(&COUNTOF_REQUIRES_ARRAY_ARGUMENT(T(&)[N]))[N];
Die Arbeit mit Threads (der Header-Datei-
Thread von std) wurde über einige der Tiny-Bibliotheken implementiert und unter Berücksichtigung der Funktionen des gesamten Compiler-Zoos und des Betriebssystems neu geschrieben. Und vielleicht
ähnelte type_traits in gewissem Maße bereits dem, was der C ++ 11-Standard verlangte. Es gab
std :: enable_if ,
std :: Integral_constant ,
std :: is_const und ähnliche Vorlagen, die bereits in der Entwicklung verwendet wurden.
namespace std { template<bool Cond, class Iftrue, class Iffalse> struct conditional { typedef Iftrue type; };
Es wurde beschlossen, alle nicht standardmäßigen und "Compiler" -Makros, Funktionen und Typen in eine separate Header-Datei
core.h zu trennen. Und im Gegensatz zur Boost-Praxis, bei der das "Umschalten" von Implementierungen mithilfe von Makros weit verbreitet ist, werden Makros, die sich auf compilerabhängige Dinge beziehen, in allen Bibliotheksdateien außer
core.h aufgegeben. Auch die Funktionalität, die nicht ohne die Verwendung von "Hacks" implementiert werden kann (Verstoß gegen den Standard, basierend auf undefiniertem Verhalten, um etwas definiert zu werden) oder für jeden Compiler einzeln implementiert wird (zum Beispiel durch seine eingebauten Makros), wurde beschlossen, nicht zur Bibliothek hinzuzufügen. um keinen weiteren monströsen (aber schönen) Schub zu erzeugen. Infolgedessen wird hauptsächlich und praktisch nur verwendet, ob
core.h verwendet wird, um festzustellen, ob Unterstützung für integriertes
nullptr vorhanden ist (da Compiler schwören, wenn reservierte Wörter überschrieben werden), Unterstützung für integriertes
static_assert (erneut, um das Überschneiden eines reservierten Wortes zu vermeiden) und Unterstützung für integrierte C ++ - Typen 11
char16_t und
char32_t .
Mit Blick auf die Zukunft kann ich sagen, dass die Idee fast ein Erfolg war, weil Das meiste, was in Boost durch harte Makros in Abhängigkeit von einem bestimmten Compiler definiert wird, wird in dieser Implementierung vom Compiler selbst in der Kompilierungsphase bestimmt.
Das Ende des ersten Kapitels. Im
zweiten Kapitel werde ich die Erzählung über die Schwierigkeiten beim Umgang mit Compilern, über gefundene Krücken und elegante Lösungen im Darm von gcc, boost und Visual Studio sowie eine Beschreibung meiner Eindrücke von dem, was ich gesehen und Erfahrungen mit Codebeispielen gesammelt habe, fortsetzen.
Danke für die Aufmerksamkeit.