Mein vielleicht wichtigstes Programmierprojekt für meine privaten Projekte und für meine Arbeit ist TheLib. Die Idee ist einfach in dieser Lib Klassen zu sammeln die wir (zwei Freunde und ich) geschreiben haben und immer und immer wieder in unterschiedlichen Projekten benutzen. Diese Klassen sind üblicherweise Wrapper um API-Aufrufe oder andere Libraries (z.B.: STL, Boost, und alle anderen) für einfachere Bedienung oder bessere Kompatibilität untereinander. Aus dieser Idee kommt auch der Name unserer Lib: TheLib – Totally Helpful Extensions (Total Hilfreiche Erweiterungen). Außerdem ist es einfach cool #include "the/exception.h"
zu schreiben.
Was ich mir aber immer wieder anhören muss: „Warum schreibst Du eine Lib? Es gibt doch schon jede Menge Libs für alle Aufgaben.“
Nun, wenn das wahr wäre, dann würde keiner von uns mehr Programme schreiben, sondern wir würden alle nur noch Programme aus fertigen Libs komponieren. Das machen wir aber (noch) nicht. Zumindest, ich mache das nicht. D.h. TheLib ist tatsächlich hilfreich. Sie ist kein Ersatz für die ganzen anderen Libs, sondern nur eine Ergänzung, eine Erweiterung.
Ein Beispiel: Strings!
Die String-Funktionen in TheLib sind nicht annäherend so mächtig wie sie sein müssten um einen Texteditor zu schreiben. Sollen sie auch nicht sein. Wir haben diese Funktionen geschrieben und etwas über die Basisfunktionen hinaus zu gehen. Die Idee ist es über einfache Funktionen Anwendungen die Möglichkeiten zu geben einfach zu bedienende Benutzungsschnittstellen zu implementieren.
Vor allem unter Linux (aber auch unter Windows) gibt es im Prinzip drei unterschiedliche Typen von Strings:
char *
und std::string
speichert ASCII oder ANSI Strings die von der lokalen Spracheinstellung des System abhängen,
char *
und std::string
speichern Multi-Byte-Strings, z.B. im UTF-8-Format, und
wchar_t *
und std::wstring
speichern Unicode Strings.
Je nach entsprechendem Stringtyp sind unterschiedlichen API-Aufrufe notwendig, z.B. um die Länge des Strings zu ermitteln::
strlen
- mehrere Aufrufe von
mbrlen
wcslen
Ein Problem ergibt sich nun bei den Fällen 1. und 2., da moderne Linuxe oft eine Spracheinstellung benutzen welche UTF-8-Strings in den Standard-Strings ablegen. Solange strings nur geschrieben, gespeichert und dargestellt werden ist das eine tolle Möglichkeit Abwärtskompatibilität zu erhalten und einen vollen Unicode Zeichensatz zu unterstützen. Allerdings, sobald etwas komplexere Aktionen durchgeführt werden sollen (sowas wie einen Teilstring auswählen) verhalten sich Implementierungen auf diesem Ansatz fehlerhaft, da sie die UTF8-Multi-Byte-Zeichen als mehrere einzelne Zeichen interpretieren.
Beispiel:
- Der Benutzer ist ein Geek und gibt „あlptraum“ als Eingabe ein.
- Das System benutzt
utf8-en
als lokale Spracheinstellung und speichert den String in einem std::string
.
- Deine Anwendung will nur das erste Zeichen haben, z.B. um es als typographische Initialie darzustellen.
- Der normale Ansatz ist nun
char* first_char = s[0];
und std::string remaining = s.substr(1);
- Da das japanische „あ“ aber zwei Bytes benutzt ist das Ergebnis: „0“ + „Blptraum“
Das Problem gilt nicht nur für japanische Zeichen, sondern logischerweise potentiell für alle Zeichen die nicht im 7-Bit ASCII abgedeckt sind. Schlimmer noch: das Problem existiert für alle Operationen die zeichenweise arbeiten, z.B. auch Vergleiche ohne Berücksichtigung von Groß-Klein-Schreibung.
Beispiel wie ein String zu Kleinbuchstaben konvertiert wird:
// Zuerst machen wir es wie die STL es vorschlägt
// http://notfaq.wordpress.com/2007/08/04/cc-convert-string-to-upperlower-case/
std::string data;
// Der Inhalt von 'data' wird nun auf "あlptraum" gesetzt und das System benutzt utf8-en als Spracheinstellung
std::transform(data.begin(), data.end(), data.begin(), ::tolower);
// Und nun ist der Inhalt von 'data': "0blptraum"
// ...
Um das Problem zu vermeiden initialisiert TheLib die System-Spracheinstellung für die Anwendung und erkennt ob eine UTF-8-Einstellung benutzt wird. Ist das der Fall, dann benutzt TheLib für alle API-Aufrufe die Varianten für Multi-Byte-Strings und die Ergebnisse entsprechen der Erwartung. Zusätzlich bietet TheLib explizite Funktionen zur Konvertierung nach und von UTF-8-Strings (z.B. für Dateizugriffe).
Natürlich braucht man TheLib nicht und das Problem zu lösen. Es gibt andere Libs (vermutlich. Ich kenne nur die IBM-Unicode-Lib, aber die scheint mir ein riesiges Ding zu sein) oder man kann sich selber Workarounds schreiben oder man ignoriert das Problem, weil es bei „den eigenen Anwendungsfällen nicht auftreten wird“. Wie dem auch sei, die TheLib das einfach machen zu lassen ist einfach praktisch. Mehr ist da nicht dahinter.