Open Source im professionellen Einsatz
Linux-Magazin 12/2014

Modernes C++ in der Praxis – Folge 19

Kurs zum Mars

C++11 kennt zwei neue Literale: Raw-String- und benutzerdefinierte Literale. Während Raw-String-Literale den Interpreter zum Beispiel davon abbringen, Backslashes in Zeichenketten zu interpretieren, schützen benutzerdefinierte Literale dereinst womöglich Nasa-Sonden vor dem Verglühen.

1316

Laut seiner Mission sollte der Mars Climate Orbiter im Jahre 1999 das Klima auf dem roten Planeten erforschen. Doch statt 150 Kilometer über der Marsoberfläche in eine Umlaufbahn einzuschwenken, tauchte die Pannen-Sonde auf 57 Kilometer hinab, wo die Atmosphäre ihr den Garaus machte. Die Analyse des Vorfalls ergab einen Einheitenfehler [1] in der Software als Ursache. Kleiner Bug, größtmögliche Auswirkung.

Die in C++11 neu eingeführten benutzerdefinierten Literale hätten das Problem womöglich verhindert. Sie verknüpfen Zahlen mit Einheiten, um diese in arithmetischen Ausdrücken zu verwenden. Ebenfalls neu an Bord von C++11 sind Raw-String-Literale, die besonders Windows-Entwicklern die tägliche Arbeit erleichtern.

Klein und praktisch

C++-Entwickler von Windows-Programmen beklagen sich häufig, dass sie ihre Dateien nicht öffnen können. Der Grund: Windows verwendet den Backslash »\« für den Zugriff auf Unterverzeichnisse. Das Backslash-Zeichen leitet aber diverse Escape-Sequenzen [2] ein. So stehen etwa die Zeichensequenzen »\n« für einen Zeilenumbruch und »\t« für einen Tabulator. Daher muss der Entwickler das Backslash-Zeichen selbst durch ein weiteres Backslash-Zeichen maskieren, damit C++ es nicht interpretiert. Kompliziert? Es wird noch besser.

Um etwa den Text »C++« mit Hilfe regulärer Ausdrücke in C++ zu beschreiben, ist der verwirrende String »C\\+\\+« notwendig. Zum einen muss der Programmierer das Pluszeichen im regulären Ausdruck durch einen Backslash, zum anderen diesen Backslash selbst als Teil des Strings maskieren.

Diese Backslash-Krämpfe gehören zum Glück der Vergangenheit an, denn dank der Raw-String-Literale schreibt der Entwickler ein einleitendes »R"(« und schiebt noch die abschließende Zeichenkette »)"« hinterher. Innerhalb der Klammern interpretiert C++ die Backslash-Symbole nicht (Listing 1).

Listing 1

Raw-String-Literale

01 #include <iostream>
02 #include <string>
03
04 int main(){
05
06   std::cout << std::endl;
07
08   std::string nat="C:\temp\newFile.txt";
09   std::cout << nat << std::endl;
10
11   // including \t \n
12   std::string raw1= std::string(R"(C:\temp\newFile.txt)");
13   std::cout << "\n" << raw1 << std::endl;
14
15   // including \t \n and using delimiter
16   std::string raw2= std::string(R"TRENNER(C:\temp\newFile.txt)TRENNER");
17   std::cout << "\n" << raw2 << std::endl;
18
19   // raw string including "
20   std::string raw3= std::string(R"(a raw string including ")");
21   std::cout << "\n" << raw3 << std::endl;
22
23   std::cout << std::endl;
24
25 }

Ruft der Entwickler das Programm aus Listing 1 auf, zeigt sich schön der Effekt der Raw-String-Literale (Abbildung 1). Die Zeichenkette »nat« in Zeile 8 verwendet unmaskierte Zeichen für einen Tabulator und einen Zeilenumbruch, mit ihr lässt sich keine Datei unter Windows öffnen. Anders sieht es mit den Strings »raw1« und »raw3« in den Zeilen 12 und 20 aus, deren Zeichen C++11 nicht in Escape-Codes verwandelt, selbst wenn das Raw-String-Literal »raw3« ein Anführungszeichen enthält.

Abbildung 1: Raw-String-Literale in Aktion. Sie nehmen Entwicklern Maskierungsarbeit ab.

In der Zeile 16 des Listings erscheint mit »raw2« ein Sonderfall. Die dort vorhandene Zeichensequenz »TRENNER« ersetzt der Entwickler wahlweise durch andere Zeichen, maximal 16 an der Zahl, wobei auch Leerzeichen darunter sein dürfen. Auf diesem Wege kann er auch Raw-String-Literale definieren, welche die Zeichensequenz »")« enthalten, die C++11 andernfalls als das Ende des Raw-String-Literals interpretieren würde.

Für Groß und Klein

Nicht immer ziehen Umrechnungsfehler gleich interplanetare Zwischenfälle nach sich, doch schon das alltägliche Umrechnen von Einheiten bietet eigene Herausforderungen (Abbildung 2). Die sollten mit modernem C++ der Vergangenheit angehören, weil es die benutzerdefinierten Literale ermöglichen, Built-in-Literale mit eigenen Suffixen zu verknüpfen. Eingebaute Literale sind Elemente, die für explizite Werte in einem Programm stehen, sei es eine Zahl, ein String oder auch eine Lambda-Funktion.

Abbildung 2: Manchmal müssen Entwickler Äpfel und Birnen vergleichen.

Bei benutzerdefinierten Literalen hängt C++ an das Built-in-Literal einen Unterstrich »_« sowie einen Suffix an, wobei Letzterer typischerweise aus Einheiten besteht. Beim benutzerdefinierten Literal »1010101_b« handelt es sich um eine boolesche Zahl, in »63_s« um eine Sekundenangabe, in »33_cent« um eine Geldmenge oder in »"Hallo"_i18n« um einen Internationalisierungs-String.

Aber genug der Theorie, Listing 2 zeigt das Ganze an einem praktischen Codebeispiel. Die Funktion des Programms ist schnell skizziert. Instanzen der Klasse »MyDistance« (Zeilen 5 bis 27) stehen für Distanzobjekte, die das Programm mit Einheiten wie Kilometer, Meter, Dezimeter und Zentimeter initialisiert und verrechnet. Bevor es aber an die Details der C++-Magie geht, ruht der Blick des Betrachters auf der »main()« -Funktion (Zeilen 47 bis 78), um ein Gesamtbild zu erhalten. Die Ausgabe des Programms liefert Abbildung 3.

Listing 2

Benutzerdefinierte Literale

01 #include <iostream>
02 #include <ostream>
03
04 namespace Distance{
05   class MyDistance{
06     public:
07       MyDistance(double i):m(i){}
08
09       friend MyDistance operator+(const MyDistance& a, const MyDistance& b){
10         return MyDistance(a.m + b.m);
11       }
12
13       friend MyDistance operator-(const MyDistance& a, const MyDistance& b){
14         return MyDistance(a.m - b.m);
15       }
16
17       friend MyDistance operator*(double m, const MyDistance& a){
18         return MyDistance(m*a.m);
19       }
20
21       friend std::ostream& operator<<(std::ostream &out, const MyDistance& myDist){
22         out << myDist.m << " m";
23         return out;
24       }
25     private:
26       double m;
27   };
28
29   namespace Unit{
30     MyDistance operator "" _km(long double d){
31       return MyDistance(1000*d);
32     }
33     MyDistance operator "" _m(long double m){
34       return MyDistance(m);
35     }
36     MyDistance operator "" _dm(long double d){
37       return MyDistance(d/10);
38     }
39     MyDistance operator "" _cm(long double c){
40       return MyDistance(c/100);
41     }
42   }
43 }
44
45 using namespace Distance::Unit;
46
47 int main(){
48
49   std:: cout << std::endl;
50
51   std::cout << "1.0_km: " << 1.0_km << std::endl;
52   std::cout << "1.0_m: " << 1.0_m << std::endl;
53   std::cout << "1.0_dm: " << 1.0_dm << std::endl;
54   std::cout << "1.0_cm: " << 1.0_cm << std::endl;
55
56   std::cout << std::endl;
57
58   std::cout << "0.001 * 1.0_km: " << 0.001 * 1.0_km << std::endl;
59   std::cout << "10 * 1_dm: " << 10 * 1.0_dm << std::endl;
60   std::cout << "100 * 1.0cm: " << 100 * 1.0_cm << std::endl;
61
62   std::cout << std::endl;
63   std::cout << "1.0_km + 2.0_dm +  3.0_dm + 4.0_cm: " << 1.0_km + 2.0_dm +  3.0_dm + 4.0_cm << std::endl;
64   std::cout << std::endl;
65
66   Distance::MyDistance work= 63.0_km;
67   Distance::MyDistance workPerDay= 2 * work;
68   Distance::MyDistance abbrevationToWork= 5400.0_m;
69   Distance::MyDistance workout= 2 * 1600.0_m;
70   Distance::MyDistance shopping= 2 * 1200.0_m;
71
72   Distance::MyDistance myDistancePerWeek= 4 * workPerDay - 3 * abbrevationToWork + workout + shopping;
73
74   std::cout << "4 * workPerDay - 3 * abbrevationToWork + workout + shopping: " << myDistancePerWeek;
75
76   std::cout << "\n\n";
77
78 }
Abbildung 3: Dank der in C++11 eingeführten benutzerdefinierten Literals können Entwickler besser mit Einheiten rechnen.

Zunächst gibt es in den Zeilen 51 bis 54 die Distanzen in den unterstützten Einheiten aus. Zugleich normiert es diese in den Zeilen 31, 34, 37 und 40 explizit auf Meter, da »MyDistance« alle Werte in dieser Einheit speichert (Zeile 25). Die Zeilen 58 bis 60 sind schon interessanter, findet hier doch eine erste elementare Arithmetik statt. Wie das Beispiel zeigt, hat der Autor sein Einmaleins gelernt, denn der tausendste Teil eines Kilometers ist genau ein Meter.

Arithmetisch geht es auch in Zeile 63 weiter, die ein paar Einheiten miteinander verrechnet. Am informativsten ist aber sicherlich das Objekt »myDistancePerWeek« in Zeile 72: Die Instanz der Klasse »MyDistance« enthält die Entfernung, die der Autor des Artikels pro Woche mit dem Auto zurücklegen muss. Die Strecke summiert sich auf rund 500 Kilometer, bestehend im Wesentlichen aus vier Fahrten zur Arbeit, reduziert um drei Abkürzungen. Dank der entsprechenden Instanzen vom Typ »MyDistance« in den Zeilen 66 bis 70 bleibt der verwendete Code gut lesbar.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 4 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • C++11

    Indem er das Zauberwort "constexpr" anwendet, erreicht der Programmierer, dass C++11 seine Ausdrücke bereits beim Kompilieren auswertet. Folgt er dabei den vorgefertigen Beschwörungsritualen, generieren solche konstanten Ausdrücke Performancegewinne.

  • C++

    Implizite Typkonvertierungen sind eine der Ursachen für undefiniertes Verhalten in C- und C++-Programmen. In Kurzform bedeutet dies, dass solche Programme alles Mögliche tun dürfen und unvorhersehbar reagieren. C++-Routinier Rainer Grimm zeigt, wie C++-Entwickler diese Falle umgehen.

  • C++11

    2014 ist ein besonderes Jahr für C++. Drei Jahre nach C++11 erfährt der Sprachstandard mit C++14 den letzten Feinschliff. Neben generischen Lambda-Funktionen und der vereinfachten Ermittlung des Rückgabetyps kann C++14 vor allem mit einem Feature punkten: Reader-Writer-Locks.

  • C++11

    Die Referenz-Wrapper bilden eine kleine, aber feine neue Bibliothek in C++11. Diese Objekte verhalten sich wie Referenzen, der Artikel erklärt die Details.

  • C++11

    Container sind nicht nur bei der Virtualisierung derzeit in aller Munde, auch dem Programmierer sind Behälter für Objekte nützlich. Die Version 11 von C++ enthält einige Algorithmen, die die Arbeit mit solchen Containern deutlich vereinfachen.

comments powered by Disqus

Ausgabe 10/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.