[X] C++09 (Teil 2) - Ein Überblick: Die Standardbibliothek
-
C++09 (Teil 2) - Ein Überblick: Die Standardbibliothek
Dies ist der zweite Teil der Serie über den wahrscheinlich 2009 erscheinenden, neuen Standard für C++.
Mit dem Technical Report (TR1) hat schon 2005 zuallererst die Standardbibliothek ein neues Gesicht bekommen, vor allem Boost diente dabei als hervorstechende Quelle der Inspiration. Der Technical Report 1 wird in C++09 vollständig enthalten sein, auch wenn teilweise Namen verändert und neue Funktionalitäten hinzugefügt werden.
Damit wird die Standardbibliothek von C++09 jener Bereich sein, in dem die meisten Neuerungen stattfinden werden. Der Bereich im C++ Standard, der ihr gewidmet ist hat sich allein von der Seitenanzahl her jetzt schon seit C++03 beinahe verdoppelt (von knapp unter 400 Seiten auf über 700 Seiten im aktuellen Working-Draft).
Der Hauptteil besteht dabei aus Erweiterungen aus dem TR1, ein weiterer großer Teil beschreibt die Unterstützung von paralleler Verarbeitung (Multithreading-Bibliothek und atomare Operationen). Ich möchte hier nicht allzu sehr ins Detail gehen, vor allem nicht Details erklären oder Schnittstellenspezifikationen aufschreiben, dafür gibt es ja den Standard selbst. In erster Linie möchte ich Beispiele geben, die zukünftige Möglichkeiten der Standardbibliothek aufzeigen.
Hinweis: Ich verwende in den Code-Beispielen bewusst exemplarisch neue Sprachfeatures und damit die teilweise recht ungewohnte Syntax von C++09. Dazu gehört vor allem:
- Die einheitliche Form der Initialisierung mit geschwungenen Klammern
- Variadic Templates
- Rvalue-Referenzen
- Die neue Form der Funktionsdeklaration
Für ein besseres Verständnis des Codes ist daher die Lektüre der entsprechenden Kapitel aus dem ersten Teil der Reihe empfehlenswert.
Inhalt
- 1. Erweiterungen aus dem TR1
- 1.1. Utilities
- 1.2. Funktionsobjekte
- 1.3. Neue Container
- 1.4. Type-Traits
- 1.5. Reguläre Ausdrücke
- 1.6. Zufallszahlen
- 2. Multithreading
- 2.1. Threads
- 2.2. Mutexte und Monitore
- 3. Datum und Zeit
- 4. System-Fehler
- 5. Integration neuer Sprachfeatures
- 5.1. Standard-Concepts
- 5.2. Noch mehr:
std::string
- 6. Erweiterungen für den TR2 und danach
- 6.1. Filesystem
- 6.2. Weitere mögliche Neuerungen
- 7. Ausblick
- 8. Verweise
1 Erweiterungen aus dem TR1
Der Technical Report 1 ist ein Dokument, in dem verschiedene Erweiterungen der C++-Standardbibliothek vorgeschlagen werden. Der TR1 wurde 2005 veröffentlicht und viele der Funktionen werden von aktuellen Compilern (bzw. Standardbibliotheken) bereits unterstützt, teils auch bereits mit Hilfe der neuen Sprachfeatures.
1.1 Utilities
Der TR1 definiert einige nützliche Klassen, die das Leben in vielen Bereichen einfacher machen. Zu ihnen gehören
- Reference Wrapper. Diese sind nützlich, um Referenzen an Funktionstemplates zu üebrgeben, die eigentlich Kopien ihrer Argumente anfertigen würden.
- Smart Pointer. Zeiger, die sich selbst um die Freigabe des Speichers kümmern.
- Tuple. Ein Container fixer Länge, der Daten verschiedenen Typs aufnehmen kann.
Beispiel
#include <functional> //Reference-Wrapper #include <memory> //Smart-Pointer #include <tuple> //Tuple #include <vector> using namespace std; template <class T> void fun (T t) { t += 21; } int main () { int i = 21; fun (ref(i)); //Einen Reference-Wrapper erstellen //i == 42 auto p = make_shared<int>(42); //new int(42) für Smart-Pointer *p = 23; //p verhält sich wie ein Zeiger. //Nur: das delete geschieht automatisch. //Dynamischen std::vector mit Inhalt 1, 2, 3, 4 erzeugen: auto vi = make_shared<vector<int>>({1,2,3,4}); //Beliebig viele verschiedene Typen in einem Tupel speichern: auto foo = make_tuple(5, 4.2, 2.3); auto bar = make_tuple("std::string"s, 'c'); //Zwei tuple-Instanzen vereinigen auto fubar = concatenate(foo, bar); //Werte auslesen auto first = get<1>(foo); //decltype(first) -> int auto second = get<2>(foo); //decltype(second) -> double } //Funktion, die mehrere Rückgabewerte hat: auto fun () -> tuple<int, vector<int>, string> { return make_tuple(5, { 5,4,3,2,1 }, "foobar"s); }
Erläuterung
Wie der Header<functional>
schon vermuten lässt, werden Reference-Wrapper hauptsächlich in Verbindung mit Funktionen höherer Ordnung verwendet. Ein weiteres Beispiel hierzu findet sich in 1.2.Smart-Pointer sind schon eine Weile im Umlauf. Der Standard von 2003 bot bereits einen:
std::auto_ptr
. Dieser hatte jedoch nicht immer die Semantik, die man erwartet hätte (Move-Semantik statt Kopiersemantik). Sein größter Nachteil war, dass er nicht in Standard-Containern verwendet werden konnte.std::auto_ptr
ist mit C++09 deprecated und kann durchstd::shared_ptr
bzw.std::unique_ptr
ersetzt werden, je nach Aufgabe.std::tuple
ist dazu gedacht,std::pair
zu ersetzen, denn es kann nicht nur zwei sondern beliebig viele Werte unterschiedlichen Typs aufnehmen.std::pair
wird mit C++09 ebenfalls deprecated sein.Status
Der TR1 ist Teil des aktuellen Working Drafts des kommenden C++-Standards.Unterstützung
Der TR1 wird von allen größeren Standardbibliothek-Implementierungen unterstützt. Da der TR1 verbietet, dass seine Klassen und Funktionen direkt in den Standardheadern eingebaut werden, muss man diese zumeist explizit aktivieren (entweder über ein Makro oder durch einen anderen Include-Pfad, bspw.<tr1/functional>
. Zu beachten ist auch, dass bestimmte Funktionen wie etwamake_shared
nicht im TR1 enthalten ist, da es auf Rvalue-Referenzen basiert (siehe Teil 1 der Reihe). Zumindest die libstdc++ implementiert in Version 4.3 einige Funktionalität mithilfe der neuen Sprachfeatures (Dazu muss man beim Kompilieren-std=c++0x
aktivieren). Allerdings gibt es keine vollständige Implementation des TR1.Proposals
N1403 - Tuple
N1450 - Smart-Pointer
N1453 - Reference-Wrapper
N1836 - Der Draft des TR11.2 Funktionsobjekte
Bereits C++03 bot einige Funktionen höherer Ordnung an, das sind Funktionen, die andere Funktionen als Argument erwarten und mit ihnen umgehen. Die Verwendung von ihnen war jedoch oft nur eingeschränkt möglich. Mit
std::function
gibt es nun einen polymorphen Funktionswrapper, der auch Zeiger auf Memberfunktionen von Klassen beinhalten kann. Außerdem wurde die Beschränkung vonstd::bind1st
undstd::bind2nd
, Argumente jeweils nur an den ersten oder zweiten Parameter binden zu können zu Gunsten vonstd::bind
, das mit Funktionen mit unbestimmter Anzahl an Parametern arbeitet, aufgegeben.Beispiel
#include <functional> //Binder, Funktionsobjekte #include <algorithm> using namespace std; //Addiert eine Zahl zu einer anderen: template <typename T> int accumulate (T value, T& acc) { return acc += value; } //Addiert zwei Zahlen und zählt mit, wie oft es aufgerufen wurde template <class T> struct Adder { int count; Adder () : count(0) {} T add (T a, T b) { ++count; return a + b; } }; int main () { vector<int> v1 { 1,2,3 }; int value = 0; for_each (v1.begin(), v1.end(), bind(accumulate<int>, placeholders::_1, ref(value))); //value == 1 + 2 + 3 //Mache aus der Function "accumulate" eine neue Funktion, die eine Zahl inkrementiert: function<int(int&)> incrementor = bind(accumulate<int>, 1, placeholders::_1); incrementor(value); //value == 7 //Auch Elementfunktion können verwendet werden: Adder<int> x; //Addiert 5 zu jedem Element transform (v1.begin(), v1.end(), v1.begin(), bind(&Adder<int>::add, &x, placeholders::_1, 5)); //x.count == 3 //v = { 6, 7, 8 } }
Erläuterung
std::bind
ist dazu gedacht, die Standardfunktionenstd::bind1st
undstd::bind2nd
zu ersetzen. Um dies zu erreichen, werden "Platzhalter" eingeführt. Aus einer Funktion, die also eigentlich zwei Argumente benötigt, kann auf diese Art und Weise eine unäre Funktion gemacht werden:void foo_binary (int, int); function<void (int)> foo_unary = bind (foo_binary, 42, placeholders::_1); foo_unary (23); //gleichbedeutend mit: foo_binary (42, 23); function<void (int)> foo_reversed = bind (foo_binary, placeholders::_2, placeholders::_1); foo_reversed (1, 2); //gleichbedeutend mit: foo_binary (2, 1);
Erläuterung
Da C++09 jedoch direkt Closures (Lambda-Funktionen) unterstützt, werden viele Bereiche, in denen momenten boost.bind eingesetzt wird, durch diese übernommen. So lässt sich obiges Beispiel mit Closures auf folgende Art und Weise implementieren:#include <functional> //Binder, Funktionsobjekte, std::reference_closure #include <algorithm> using namespace std; int main () { vector<int> v1 { 1,2,3 }; int value = 0; for_each (v1.begin(), v1.end(), [&value](int element) (value += element)); //value == 1 + 2 + 3 //Speichere ein (Reference-)Closure reference_closure accumulate = [](int value, int& acc) -> int { return acc += next; }; //Mache aus dem Closure "accumulate" eine neue Funktion, die eine Zahl inkrementiert: function<int(int&)> incrementor = bind(accumulate, /* inkrementiere um */ 1, placeholders::_1); incrementor(value); //value == 7 }
Erläuterung
Dieses Beispiel zeigt, dass sich mit Closures viele Gebiete, die derzeit von umständlich zu erstellenden Funktionsobjekten abgedeckt werden, einfacher implementieren lassen.Status
siehe 1.1Unterstützung
siehe 1.1
Closures und damit auchstd::reference_closure
werden allerdings noch von keinem Compiler unterstützt.Proposals
N1402 - Polymorphe Funktionsobjekt-Wrapper
N1836 - Der Draft des TR11.3 Neue Container
TR1 definiert drei neue Container, die dieselbe Schnittstelle wie die anderen Standardcontainer anbieten. Es sind dies:
std::array
für nicht-dynamische Arrays fixer Größestd::unordered_set
, die gehashte Version vonstd::set
std::unordered_map
, die gehashte Version vonstd::map
- C++09 enthält außerdem noch einen zusätzlichen Container,
std::forward_list
Beispiel
#include <array> #include <unordered_map> #include <unordered_set> #include <forward_list> int main () { std::array<int, 20> array; std::unordered_map<string, int> unordered_map; std::unordered_set<double> unordered_set; std::forward_list<int> li = { 1, 2, 3, 4 }; }
Erläuterung
std::unordered_map
undstd::unordered_set
lassen sich - vom Interface her - ebenso verwenden wie die Standardcontainerstd::map
undstd::set
. Statt einen Vergleich mit dem Kleiner-als-Operator durchzuführen (standardmäßiges Verhalten beistd::map
undstd::set
), wird hier eine Hash-Funktion verwendet. Um mit benutzerdefinierten Typen arbeiten zu können, muss daher eine Spezialisierung vonstd::hash
(Header<hash>
) implementiert werden.
std::forward_list
ist eine einfach verkettete Liste, im Gegensatz zustd::list
, die doppelt verkettet ist.Status
siehe 1.1Unterstützung
siehe 1.1Proposals
N1456 - Hashtables
N1548 - Array
N1836 - Der Draft des TR1
N2545 - Singly-linked list1.4 Type-Traits
Type-Traits sind ein beliebtes Mittel der Template-Metaprogrammierung, das hilft, generischen Typen bestimmte Constraints zu setzen. Ob und wie weit sich dies durch den Einsatz von Konzepten (Siehe hierzu Teil 1 der Reihe) ersetzen lässt, wird sich zeigen.
Beispiel
#include <type_traits> using namespace std; template <typename T, class Base, class Derived> struct Foo { static_assert (is_integral<T>::value, "T ist nicht integral!"); static_assert (is_base_of<Base, Derived>::value, "Erwarte voneinander abgeleitete Typen!"); }; template <typename T> void foo (T const& t, typename enable_if<is_pod<T>>::type * = 0) { } struct Virtual { virtual ~Virtual () = default; }; int main () { //OK, 5 ist plain-old-data foo(5); //Ups, polymorphe Klasse ist kein POD - Kompilierfehler foo(Virtual()); }
Erläuterung
Es stellt sich bei Type-Traits natürlich die Frage, inwieweit sie durch Konzepte (siehe Teil 1 der Reieh) abgelöst werden. Diese bieten ein eleganteres Interface, um Template-Parametern gewisse Constraints aufzuerlegen.Status
siehe 1.1
Im TR1 allerdings nicht enthalten sindstd::enable_if
,std::conditional
undstd::decay
. Diese sind allerdings im Working-Draft des kommenden C++-Standards beinhaltet.Unterstützung
Die im TR1 nicht enthaltenen Type-Traits befinden sich auch nicht im Namensbereichstd::tr1
. Als Alternative lassen sich bisweilen die entsprechenden Type-Traits aus der Boost-Bibliothek verwenden. Fürstd::decay
gibt es allerdings meines Wissens nach noch keine Implementierung.Proposals
N1424 - Type Traits
N1836 - Der Draft des TR1
N2240 -std::enable_if
undstd::conditional
N2244 -std::decay
1.5 Reguläre Ausdrücke
Reguläre Ausdrücke oder "regular expressions" (RegExp, RegEx) sind Zeichenketten, die eine Art Filterkriterium für Texte darstellen. So ist es möglich mit einem regulären Ausdruck einen Text zu beschreiben, der mit einem "A" beginnt, nur auch Buchstaben besteht und mit einem Punkt endet. Einsätze sind dabei vor allem komplizierte Suchausdrücke, aber auch Ersetzungen, indem man die zu suchenden Textteile als reguläre Ausdrücke definiert. Reguläre Ausdrücke sind in vielen Programmiersprachen gang und gäbe.
Beispiel
#include <regex> #include <iostream> #include <string> using namespace std; int main () { char const* text = "Ein langer Text\tmit Trennzeichen; Tja"; smatch matches; //typedef für match_results<string::const_iterator> //Splitte Text nach Trennzeichen if (regex_search(text, matches, regex("[ ,.\\t\\n;:]")) { for (auto match: matches) //decltype(match) -> std::sub_match<std::string::iterator> bzw. std::ssub_match { cout << match << '\n'; } } }
Erläuterung
Ein regulärer Ausdruck kann auf verschiedene Art und Weisen implementiert sein. Die Standardbibliothek liefert die Möglichkeit, zu testen, welche Art von regulären Ausdrücken sie unterstützt, dieses Feature wurde im Beispiel der Einfachheit halber jedoch nicht verwendet. Die Klassestd::regex
bzw.std::basic_regex<>
dient dafür, die Engine, die im Hintergrund Strings parst, zu abstrahieren. Mithilfe verschiedener Standardfunktionen wiestd::regex_search
oderstd::regex_replace
lässt sich so mit regulären Ausdrücken arbeiten.Ein eigener Container, der Matches für reguläre Expressions aufnehmen kann, ist dabei
std::match_results
, bzw. die fürstd::string
explizit spezialisierte Versionstd::smatch
. Dastd::match_results
einen Container darstellt, ist es möglich, mittels Iteratoren einzelne Matches (std::sub_match
bzw. fürstd::strings
std::ssub_match
) auszulesen. Dieses ist im Prinzip einfach vonstd::pair<Iterator, Iterator>
abgeleitet, besitzt aber einen Konvertierungsoperator nachstd::string
.Ein weiteres Feature von regulären Ausdrücken in C++09 ist, dass es auch mit den C++ Locales zusammenarbeitet. Eine eigene Regex-Traits Klasse (
std::regex_traits
), die für einzelne reguläre Ausdrücke individualisiert werden kann, sorgt dabei für die Anbindung an bestimmte Locales - dabei ist anzumerken, dass eigene Spezialisierungen vonstd::regex_traits
auch non-C++ Locales verwenden können. Regex-Traits erlaubt außerdem, die Syntax von regulären Ausdrücken den eigenen Gewohnheiten anzupassen.Alles in allem stellt die C++09 RegEx-Bibliothek ein mächtiges Werkzeug dar, um mit regulären Ausdrücken auch international arbeiten zu können.
Status
siehe 1.1Unterstützung
Die C++09 RegEx-Bibliothek wird, obwohl sie bereits im TR1 enthalten war, meines Wissens noch von keinem Compiler unterstützt.Proposals
N1429 - Reguläre Ausdrücke
N1836 - Der Draft des TR11.6 Zufallszahlen
Der TR1 definiert ein mächtiges Interface, um Zufallszahlen zu erzeugen. Er definiert dabei sogenannte "Engines", die Zufallszahlen nach bewährten Algorithmen erzeugen und stellt diverse Verteilungen zur Verfügung, die mit diesen Engines arbeiten können.
Beispiel
#include <random> using namespace std; int main () { //Zufallszahlen-Seeder: Erwartet in dem Fall Eingaben von std::cin seed_seq seeder {istream_iterator<int>(cin), istream_iterator<int>()}; mt19937 mersenne_twister_engine {seeder}; ranlux24 discard_block_engine {seeder}; knuth_b shuffle_order_engine {seeder}; bernoulli_distribution distribution_a {0.25}; uniform_int_distribution<int> distribution_b {0, 99}; weibull_distribution<double> distribution_c; bool a = distribution_a (discard_block_engine); int b = distribution_b (shuffle_order_engine); double c = distribution_c (mersenne_twister_engine); }
Status
siehe 1.1Unterstützung
Die tatsächlichen Bezeichnungen weichen zum Teil vom Vorschlag im TR1 ab. Außerdem wird meines Wissens nach die Zufallszahlen-Bibliothek von C++09 noch von keinem Compiler zu 100% unterstützt.Proposals
N1836 - Der Draft des TR1
N2111 - Die Zufallszahlen-Bibliothek2 Multithreading
C++09 bietet den langersehnten Multithreading-Support. Dies geschieht einerseits über die Multithreading-Bibliothek, die Objekte für Threads, Mutexe und Locks und Monitore (Condition Variables) bereitstellt.
2.1 Threads
In Zeiten, in denen Prozessoren mehrere Kerne haben, ist es sinnvoll, Berechnungen auf diese zu verteilen. Auch Programme auf Single-Core-Prozessoren können durch den Einsatz von Threads verbessert werden, indem einzelne Komponenten des Programms voneinander entkoppelt jeweils den eigenen Anforderungen entsprechend Systemzeit verbrauchen können.
Beispiel 1 - Neue Threads erstellen
#include <thread> #include <functional> #include <algorithm> #include <iostream> #include <iterator> #include <vector> void bar () { } struct MyThread { void start (int data) { //An den nächsten Thread weitergeben this_thread::yield(); } }; int main () { //Starte neuen Thread mit Aufruf von bar(); std::thread thread_a {bar}; MyThread instance; //Starte neuen Thread mit Aufruf von instance.start(42); std::thread thread_b {std::mem_fun(&MyThread::start), &instance, 42}; std::vector<int> data; //Kopiere in einem neuen Thread Daten von cin in den vector std::thread reader ( [&]() ( std::copy(std::istream_iterator<int>(std::cin), std::istream_iterator<int>(), std::back_inserter(data)) ) ); //Warte auf den Eingabe-Thread reader.join(); //Gib aus, was die Hardware an Parallelität verträgt std::cout << std::thread::hardware_concurrency() << std::endl; }
Erläuterung
Dastd::thread
mithilfe von Variadic Templates implementiert ist, können beliebige Funktionen und Funktionsobjekte als Eingangspunkte für einen Thread definiert werden. Sowohl freistehende Funktionen wiebar
als auch Elementfunktionen wieMyThread::start
und sogar Lambda-Funktionsobjekte (siehe dazu den ersten Teil der Reihe) können als eigener Thread ausgeführt werden. Das Interface vonstd::thread
erinnert ein bisschen an die entsprechende Boost-Bibliothek.Beispiel 2 - Einmalige Initialisierung
#include <thread> #include <array> std::once_flag init_once; void general_init_routine (int data) { std::cout << "Führe allgemeine Initialisierungsaufgaben (Singletons?) durch\n"; } void thread_fun (int x) { std::call_once (init_once, general_init_routine, x); //general_init_routine wurde mit Sicherheit ausgeführt und beendet. } int main () { array<thread, 3> threads = { thread {thread_fun, 1}, thread {thread_fun, 2}, thread {thread_fun, 3} }; //Warte auf alle Threads for (auto thread: threads) thread.join(); }
Erläuterung
In diesem Beispiel werden drei Threads erstellt, die jeweils versuchen, eine gemeinsame Initialisierungsfunktion aufzurufen. Mithilfe vonstd::once_flag
undstd::call_once
ist sichergestellt, dass diese Funktion tatsächlich nur ein einziges Mal aufgerufen wird. Alle anderen Threads müssen mit ihrer Ausführung außerdem warten, bis diese Funktion auch tatsächlich aufgerufen wurde. In diesem Beispiel könntegeneral_init_routine
also entweder mit 1, 2 oder 3 aufgerufen werden.Status
Die Multithreading-Bibliothek ist Teil des aktuellen Working-Drafts.Unterstützung
Die Boost-Bibliothek Boost.Threads ist ähnlich aufgebaut, jedoch nicht vollständig kompatibel mit der C++09-Multithreading-Bibliothek. Dastd::thread
sehr stark auf neue Sprachfeatures baut, und diese nur von wenigen Compilern (in der Form nur von GCC 4.3) unterstützt werden, ist derzeit keine Implementation verfügbar.Proposals
N2139 - Hintergründe
N2410 - Thread-Safety in der Standardbibliothek
N2427 - Atomare Operationen
N2480 - Eine informelle Erklärung des neuen C++-Speichermodells
N2497 - Die Multithreading-Bibliothek2.2 Mutexe und Monitore
Mutexe und Monitore dienen der sicheren Verwendung von Daten zwischen mehreren Threads. Sie verhindern Wettlaufsituationen, das sind Situationen, in denen das Ergebnis einer Operation vom zeitlichen Verhalten bestimmter Einzelsituationen abhängt. Ein sicherer Umgang mit ihnen verhindert außerdem Deadlocks, das sind Situationen, in denen Threads auf Informationen warten, die sie jedoch erst durch einen jeweils anderen Thread bekommen würden. Da dieser andere Thread jedoch selbst auf Informationen wartet (zyklische Abhängigkeit), stoppt das Programm.
Beispiel 1 - Mutexe und Locks
#include <thread> #include <mutex> #include <iostream> #include <array> #include <string> std::mutex input_mutex, output_mutex; void thread_fun () { for (;;) { std::string txt; { std::lock_guard<std::mutex> lock {input_mutex}; std::cin >> txt; } { std::lock_guard<std::mutex> lock {output_mutex}; std::cout << txt << '\n'; } } } int main () { std::array<std::thread, 2> threads = { std::thread {thread_fun}, std::thread {thread_fun} }; for (auto thread: threads) thread.join(); }
Erläuterung
Mutex ermöglichen die sichere Verwendung von Objekten, auf die mehrere Threads gleichzeitig zugreifen wollen. Dazu bieten sie die Funktionenlock
,try_lock
undunlock
an. Eine bestimmte Art von Mutexen,std::timed_mutex
undstd::recursive_timed_mutex
bieten zusätzlich die Möglichkeit, die Resource nur für eine bestimmte Zeitspanne zu blockieren.
Das automatische Sperren und entsperren eines Mutex (RAII) geschieht mit Hilfe der beiden Lock-Klassenstd::lock_guard
undstd::unique_lock
, wobeistd::unique_lock
am ehesten demboost::scoped_locked
entspricht.
Mit den Standard-Funktionenstd::try_lock
undstd::lock
ist es zudem möglich, eine beliebige Anzahl von Locks auf eine sichere Weise gleichzeitig zu sperren.Beispiel 2 - Monitore
#include <thread> #include <mutex> #include <condition_variable> #include <queue> #include <iostream> std::mutex guard; std::condition_variable monitor; std::queue<int> data; void produce_data () { for (;;) { int x; std::cin >> x; //Threadsicheres Speichern der neuen Daten std::lock_guard<std::mutex> lock {guard}; data.push_back(x); //Signalisiere, dass neue Daten da sind if (data.size()) monitor.notify_one(); } } void consume_data () { for (;;) { int x; { //Versuche, Daten zu lesen std::unique_lock<std::mutex> lock {guard}; //Warte, bis neue Daten ankommen monitor.wait(lock, [&]()(!data.empty())); //Daten sind verfügbar x = data.front(); data.pop(); } std::cout << "Eingabe: " << x << std::endl; } } int main () { std::thread producer {produce_data}; std::thread consumer {consume_data}; producer.join(); consumer.join(); }
Erläuterung
Der Namecondition_variable
rührt daher, dass eine bestimmte Bedingung erfüllt sein muss, bis ein Thread seine Arbeit wieder aufnimmt: Diese Bedingung stellt in obigem Beispiel die Lambda-Funktion[&]()(!data.empty())
dar. Der aktuelle Thread soll also solange warten, bisdata.empty()
nicht mehrtrue
liefert, also solange bis neue Daten eingetroffen sind. Diese Benachrichtigung erhält er durch den anderen Thread (monitor.notify_one()
).
wait
entsperrt gleichzeitig den Lock, so dass der Producer-Thread diequeue
mit neuen Daten füllen kann. Sobald die Bedingung fürwait
erfüllt ist, wird der Lock allerdings wieder hergestellt.Status
Siehe 2.1.Unterstützung
Siehe 2.1.Proposals
N2406 - Hintergründe zu Mutexen, Locks und Monitoren3 Datum und Zeit
Da die C++-Multithreading-Bibliothek auch Funktionen wie Sleep und zeitabhängiges Warten auf Resourcen ermöglichen soll, musste für sie eine eigene Date-Time-Biblothek geschaffen werden.
Die meisten Funktionen sind bereits in der Boost Date-Time-Bibliothek implementiert. Die Date-Time-Bibliothek, die in C++09 inkludiert werden soll, sieht sich dabei als Vorgängerin für eine noch umfassendere Bibliothek im Technical Report 2.Beispiel 1 - Date-Time in Verbindung mit Threads
#include <date_time> #include <thread> template <class Lock> void thread (Lock lock) { std::this_thread::sleep(std::seconds(1)); std::recursive_timed_mutex rtm; rtm.time_lock(std::milliseconds(20)); }
Erläuterung
Die Date-Time-Bibliothek führt drei Arten von Typen ein:- Zeitpunkte. Ein Zeitpunkt repräsentiert einen Moment im Zeitkontinuum.
- Dauer. Eine Dauer ist nicht von einem Zeitpunkt abhängig und repräsentiert eine gewisse Zeitlänge. Diese kann auch negativ sein.
- Uhren. Ein Uhren-Typ ist ein Interface zu einem Gerät, dass einen Zeitpunkt zurückgibt.
Die konkreten Typen in C++09 sollen sein:
- utc_time - ein Zeitpunkt in einer Auflösung von Nanosekunden.
- hiresolution_clock - Eine Uhr, die die momentane utc_time zurückgibt.
- hours, minutes, seconds, milliseconds, microseconds und nanoseconds. Typen, die eine Zeitdauer repräsentieren.
Mit diesen Typen kann man intuitiv arbeiten.
Beispiel 2 - Intuitives Interface
#include <date_time> using namespace std; int main () { utc_time now = hiresolution_clock::universal_time(); utc_time tomorrow = now + hours(24); hours aday {24}; aday += minutes {10};//Kompilierfehler: Verlust an Genauigkeit minutes m {10}; m += aday; //OK, Stunden können in Minuten umgerechnet werden }
Erläuterung
Ein Objekt vom Typutc_time
, dass von seinem Standardkonstruktor initialisiert wird, repräsentiert die UTC-Zeit 1970-01-01 00:00:00.000000000.Status
Die Date-Time-Library wurde gleichzeitig mit der Multithreading-Bibliothek in den Working-Draft aufgenommen. Allerdings wurde sie mittlerweile wieder aus dem Working-Draft entfernt, da zu viele Details nicht geklärt werden konnten. Eine Erklärung für ihr Verschwinden findet sich hier.Unterstützung
Mir sind keine Compiler bekannt, die diese Features unterstützen.Proposals
N2411 - Date/Time für C++0x
N2492 - Date/Time als Teil der Multithreading-Bibliothek
N2498 - Custom Time Duration Support4 System-Fehler
Vor allem im Hinblick auf die Dateisystem-Bibliothek, die mit dem Technical Report 2 eingeführt werden soll, ist es nötige, Fehlermeldungen des Systems in C++-Ausnahmen zu verwenden. Aber auch die Multithreading-Bibliothek profitiert davon, System-Fehlermeldungen über eine standardisierte Schnittstelle zu präsentieren. Der Standard stützt sich dabei auf die POSIX-Fehlerflags. Auch die Multithreading-Bibliothek soll Systemfehler über diese Schnittstelle an die Anwendung weiterleiten.
*Beispiel 1 - Verwendung von
system-error
*#include <system_error> #include <thread> #include <iostream> void foo () {} int main () { try { std::thread thr { foo }; } //Wenn der Thread nicht gestartet werden konnte... catch (std::system_error& error) { std::cerr << error.what(); } }
Beispiel 2 - Eigene Erweiterungen
#include <system_error> namespace std { struct my_error_catalog : virtual public std::error_catalog { typedef std::error_catalog base_type; static value_type const out_of_ink = 20001; static value_type const out_of_paper = 20002; virtual auto is_valid_value (value_type v) const throw () -> bool { if (!base_type::is_valid_value(v)) return v == out_of_ink || v == out_of_paper; return true; } virtual auto str (value_type v) const throw () -> char const* { char const* ink = "Out of Ink"; char const* paper = "Out of Paper"; if (base_type::is_valid_value(v)) return base_type::str(v); switch (v) { case out_of_ink: return ink; case out_of_paper: return paper; default: return nullptr; } } virtual auto last_value() const throw() -> value_type const { return out_of_paper; } } } //... if (printer.ink_level() < 5percent) { throw system_error { std::my_error_catalog::out_of_ink, my_error_catalog() }; }
Erläuterung
Eine der stärken der System-Error Schnittstelle ist es,std::locales
zu unterstützen. Die Vorgabe lautet außerdem, dass die Nachrichten, diewhat
liefert, eine Fehler-Diagnose erleichtern sollen.Status
Dieses Feature ist Teil des Working-Drafts. Allerdings gibt es bereits Vorschläge, die Schnittstelle zu modifizieren bzw. zu erweitern. So wie es aussieht sind auch hier einige Unklarheiten aufgetreten, und es existiert bereits der Vorschlag,system_error
wieder aus dem Working-Draft zu entfernen, wie bereits mit der Date/Time-Bibliothek geschehen.Unterstützung
Mir sind keine Compiler bekannt, die dieses Feature unterstützen.Proposals
N2303 - System-Error-Unterstützung
N2538 - Entfernen der System-Error-Unterstützung5 Integration neuer Sprachfeatures
Durch die vielen neuen Möglichkeiten, die C++09 bietet - von Variadic Templates, Rvalue-Rereferenzen bis zur Unicode-Unterstützung, war es ebenso nötig, bereits bestehende Bibliotheken dem Wandel der Sprache anzupassen. Neben der Einführung von Konzepten und Concept-Maps für die Klassen der STL und der Implementierung von Move-Semantik durch Rvalue-Referenzen, die weitgehend unsichtbar im Hintergrund arbeitet, gibt es auch einige durchaus sichtbare und erwähnenswerte Features, die das Arbeiten mit der "alten" Standardbibliothek erleichtern sollen.
5.1 Standard-Concepts
Um das Rad nicht ständig neu erfinden zu müssen, definiert die Standard-Bibliothek einige Konzepte wie LessThanComparable, die häufig gebraucht werden. Waren es einst bestimmte Bedingungen, die nur formal an Konzepte wie Iteratoren, Container usw. gestellt wurden, sind diese nun auch explizit im Code formulierbar.
Beispiel
#include <concepts> struct MyOrder { int i; }; bool operator == (MyOrder const& a, MyOrder const& b) { return a.i == b.i; } bool operator < (MyOrder const& a, MyOrder const& b) { return a.i < b.i; } template <typename T> requires EqualityComparable<T> && LessThanComparable<T> void foo (T a, T b) { a == b; a != b; //automatisch generiert - Definition in EqualityComparable a >= b; //automatisch generiert - Definition in LessThanComparable } struct MyInteger { long long value; MyInteger (long long v) : value(v) {} MyInteger& operator+= (MyInteger x) { value += x.value; return *this; } //... }; template <Arithmetic T> void bar () { T a{1}, b{2}, c{3}; a = b + c; //operator+ wird automatisch erzeugt. }
Status
DaConcepts
selbst noch nicht im Working-Draft enthalten sind, ist auch der Header<concepts>
noch nicht mit dabei. Bis zur Veröffentlichung des neuen Standards wird sich dies jedoch mit Sicherheit noch ändern.Unterstützung
ConceptGCC unterstützt die<concepts>
-Bibliothek. Allerdings kann ConceptGCC noch nicht keine sogenannte "Default-Implementationen" ausconcepts
erstellen. (D.h. die obigen Beispiele funktionieren nicht.)Proposals
N2036 - Hintergründe
N2037 - Concepts für die Standardbibliothek (Einführung)
N2572 - Die wichtigsten Concepts5.2 Noch mehr: std::string
Die Funktionalität rund um
std::string
wird noch weiter ausgebaut, um ihn endgültig zur Standard-Stringklasse für C++ zu machen und von den lästigen nullterminierten Char-Zeigern wegzukommen.*Beispiel 1 - Schnittstellen für
std::string
*#include <locale> #include <fstream> #include <string> int main () { std::string loc { "fr_FR.UTF-8" }; std::locale myLocale { loc }; //Statt loc.c_str() std::string filename { "foo.txt" }; std::fstream file { filename };//Dito hier file.close(); //Auch filebufs unterstützen jetzt std::strings file.rdbuf()->open(filename, ios::out); }
Erläuterung
Mit diesem Feature wird das Interface der Standardbibliothek vereinheitlicht und anfängerfreundlicher gestaltet.*Beispiel 2 -
operator<<
*#include <string> int main () { std::string a, b { "strings" }; a << "Aneinander" << 'r' << "eihen von " << b; }
Erläuterung
Das Konkatenieren von Strings wird somit übersichtlicher als die Alternative mitoperator+=
. Allerdings wirdoperator<<
nicht die Konversion von anderen Typen nachstd::string
ermöglichen.Beispiel 3 - neue Stringfunktionen
#include <stdexcept> #include <iostream> #include <string> int main () { std::string number; cin >> number; try { long twice = std::stol(number) * 2; double half = std::stod(number) / 2; string output = "Zweimal " + number + " ist " + to_string (twice) + ", die Hälfte davon ist " + to_string(half); } catch (std::invalid_argument const&) { //Konnte number nicht in long oder double konvertieren } catch (std::out_of_range const&) { //Number war zu groß/klein für long oder double } }
Erläuterung
Mit den Standardfunktionenstd::stoi
,std::stol
,std::stoul
,std::stoll
,std::stoull
,std::stof
,std::stod
,std::stold
undstd::to_string
wird endlich "von Zahl nach String und zurück" ohne Stringstreams ermöglicht.Status
Die neuen Schnittstellen fürstd::strings
sind bereits im Working-Draft enthalten. Andere Erweiterungen, die von Neuerungen in der Sprache selbst abhängen, werden frühestens dann inkludiert, wenn das entsprechende Feature in den Working-Draft kommt.operator<<
ist leider noch nicht im Working-Draft und es ist nicht sicher, ob er es noch rechtzeitig in den Standard schafft. Die numerischen Konvertierungsfunktionen sind bereits Teil des Working-Drafts.Proposals
N1981 - Einheitliche Verwendung vonstd::string
N2233 -basic_string operator<<
6 Erweiterungen für den TR2 und danach
Während noch fleißig am kommenden Standard gearbeitet wird, lässt sich auch schon ein bisschen was über den Technical Report 2 sagen: Unterstützung für Dateisystem-Operationen wird definitiv darin enthalten sein. Anderes, wie eine Netzwerkbibliothek, befindet sich noch in einer sehr frühen Planungsphase, auch wenn das Komitee den Neuerungen gegenüber recht aufgeschlossen ist. Das liegt wohl auch hauptsächlich daran, dass die meiste Arbeitszeit nun in den kommenden Standard fließt und nicht in spekulative Erweiterungen danach
5.1. Dateisystem
Die Dateisystem-Bibliothek, die in den Technical-Report 2 aufgenommen werden soll, orientiert sich stark an Boost.Filesystem.
Beispiel
#include <iostream> #include <filesystem> using std::tr2::sys; using std::cout; int main (int argc, char** argv) { std::string p { argc <= 1 ? "." : argv[1] }; if (is_directory(p)) { for (directory_iterator iter(p); iter != directory_iterator(); ++iter) { cout << iter->path().leaf() << ' '; //Dateinamen anzeigen if (is_regular(iter->status())) cout << file_size(iter->path()); cout << '\n'; } } else cout << (exists(p) ? "Found: " : "Not Found: ") << p << '\n'; }
Status
Dieses Feature ist bereits als Teil des TR2 angenommen worden. Der TR2 steht allerdings noch weit vor der Veröffentlichung.Unterstützung
Mir sind keine Compiler bekannt, die dieses Feature unterstützen. Allerdings funktioniert Boost.Filesystem ähnlich.Proposals
N1975 - Quelle und Erläuterung des Beispiels5.2 Weitere mögliche Neuerungen
Über weitere Bibliotheken, die in den TR2 aufgenommen werden sollen, lassen sich im Moment nur Vermutungen aufstellen. Möglicherweise wird die Boost.Asio-Bibliothek für C++-Netzwerkfähigkeiten zuständig sein, Boost.Any könnte ebenso aufgenommen werden.
Sollten Ihnen als LeserInnen Bibliotheken einfallen, die Sie für besonders aufnahmenswert empfinden, folgen Sie dem "Call for Proposals" - Wer weiß, vielleicht kommt die eine oder andere Idee auch durch
Proposals
N2175 - Proposal für eine Netzwerkbibliothek
N1393 - Any Library Proposal
N2044 - Shared Memory Proposal
N2276 - Thread Pools und Futures7 Ausblick
In den folgenden Artikeln dieser Reihe beschäftige ich mich eingehender mit bestimmten Features, zu denen ich nun einen groben Überblick gegeben habe. Das erste große Feature werden. Die Auswahl, nach der ich vorgehe, ist dabei einfach:
Bereits verfügbare Features zuerst, bevorzugt Änderungen, die einen großen Einfluss auf zukünftigen Programmierstil haben werden.
Die Wahl fällt damit zunächst auf Variadic Templates, weil diese das nächste große Feature sind, das bereits implementiert ist und weil es wahrscheinlich ist, dass es in naher Zukunft von mehreren Compilern unterstützt wird, da es nicht sonderlich schwer zu implementieren ist.
8 Verweise
ISO - C++ Library Working Group (LWG) Status Report
Implementierungsstatus der libstdc++
C++0X - The New Face of Standard C++
Daraus: Minimal Garbage Collection Support - The Latest Proposal (I-III)
N2151 - Variadic Templates für die Standardbibliothek
-
so, ich habe das hier jetzt auch noch ein bisschen erweitert. falls noch wer wünsche hat, dort:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2433.html
sind alle proposals für die standardbibliothek zu finden. (auch sachen, die erst im TR2 oder danach kommen könnten)
-
in der Regel die Header mit dem Präfix "tr1/" versehen werden
Die ganzen Templates usw. des TR1 sind nicht im tr1-Unterverzeichnis. Sie stecken ganz normal in bekannten Headern, die es schon im std-Namespace gibt. Smartpointre sind z.B. nicht in <tr1/memory> sondern in <memory>. Steht ja auch im TR1-Draft so drin. Wird auch von Boosts TR1 und von Dinkumware so umgesetzt:
http://www.dinkumware.com/manuals/?manual=compleat&page=memory.html
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1745.pdf (hier habe ich nichts von <tr1/...> gefunden.)
Ob die TR1-Implementierung physikalisch woanders liegt, spielt für den Benutzer keine Rolle, ist ein Implementierungsdetail. Inkludieren tut man keine Headers aus einem Unterverzeichnis.
-
hm die tatsächliche regelung ist anscheinend ein kompromiss:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1836.pdf
1.3:TR1/Last Revision schrieb:
3 Even when an extension is specified as additions to standard headers (the third category in section 1.2), vendors should not simply add declarations to standard headers in a way that would be visible to users by default. [Note: That would fail to be standard conforming, because the new names, even within a namespace, could conflict with user macros. —end note] Users should be required to take explicit action to have access to library extensions.
4 It is recommended either that additional declarations in standard headers be protected with a macro that is not defined by default, or else that all extended headers, including both new headers and parallel versions of standard headers with nonstandard declarations, be placed in a separate directory that is not part of the default search path.
wir natürlich im artikel nachgebessert.
-
So, endlich habe ich den Teil zu Threads und den (kleinen) Rest fertiggestellt.
Wenn noch jemand Extra-Wünsche hat, hier ist die liste mit allen vorschlägen, die im laufe der letzten jahre beim komitee eingetrudelt sind.ansonsten bitte ich einmal die multithreading-spezialisten, sich durchzusehen, ob ich auch halbwegs nachvollziehbar geschrieben habe.
-
Sieht schon gut aus. Hast du schon mal auf den Counter für den ersten Teil angesehen? http://magazin.c-plusplus.net/counter Ist wirklich nicht schlecht für den ersten Tag.
Smart Pointer. Sie dienen automatischem Management von dynamischem Speicher.
Wer Smart-Pointers schon kennt der weiß was gemeint ist, die anderen aber nicht. Wie wäre es mit:
Smart Pointer. Zeiger die sich selbst um die Freigabe des Speichers kümmern.
Tuple. Eine Art Container, der jedoch Daten auch verschiedenen Typs beinhalten kann.
Hier würde ich noch erwähnen, dass sie eine konstante Länge haben.
auto p = make_shared<int>(42); //Entspricht new int(42)
da würde ich im Kommentar schreiben:
new int(42) für Smart Pointer
Bei deiner Version könnte man sich fragen wieso man nicht einfach new int(42) nimmt.
std::tuple ist im Prinzip einfach eine generischere Version von std::pair,
Generisch ist auch std::pair. Du meinst beliebig lang.
der auch Elementfunktionen beinhalten kann
Was ist eine Elementfunktionen? Meinst du einen Methodenzeiger?
zu gunsten einer generischeren Variante, std::bind, aufgegeben.
Gleiche Bemerkung zu "generischeren" wie weiter oben.
std::unordered_map und std::unordered_set lassen sich - vom Interface her - ebenso verwenden wie die Standardcontainer std::map und std::set.
Nur, dass sie anstatt eines op< eine hash Funktion erwarten und -wie der Name schon suggeriert- sind ihre Elemente nicht geordnet.
void start (int data)
{
//An den nächsten Thread weitergeben
this_thread::yield();
}Was weiter geben?
Ist das mem_fun da notwendig?
//Gib aus, was die Hardware an Parallelität verträgt
Du meinst wie viele Prozessoren es gibt?
Da std::thread mithilfe von Variadic Templates implementiert ist, können beliebige Funktionen und Funktionsobjekte als Eingangspunkte für einen Thread definiert werden.
Das geht auch mit einfachen Templates. Du musst schon sagen wo die Variadic Templates hier mitmischen.
//general_init_routine wurde mit Sicherheit ausgeführt.
Und beendet!
Der aktuelle Thread soll also solange warten, bis data.empty() nicht mehr true liefert, also solange bis neue Daten eingetroffen sind.
Wie oft überprüft er die Bedingung oder geht das irgendwie anders?
-
sorry, werde schauen, dass ich nächste woche zeit hab, die änderungen einzuarbeiten. bei mir geht gerade alles drunter und drüber ^^'
Ben04 schrieb:
std::tuple ist im Prinzip einfach eine generischere Version von std::pair,
Generisch ist auch std::pair. Du meinst beliebig lang.
hm ich dachte in etwa so: std::pair ist generisch, aber std::tuple ist noch viel generischer - weil es eben noch viel allgemeiner implementiert werden kann.
bevor hier ein streit darüber entfacht, was genau "generisch" bedeutet, werde ich wohl versuchen, das phänomen mit mehr worten zu beschreiben als "generisch" (auch wenn es IMHO passt )Was ist eine Elementfunktionen? Meinst du einen Methodenzeiger?
da es keine referenzen auf methoden gibt, klar. wird aber trotzdem verständlicher formuliert.
std::unordered_map und std::unordered_set lassen sich - vom Interface her - ebenso verwenden wie die Standardcontainer std::map und std::set.
Nur, dass sie anstatt eines op< eine hash Funktion erwarten und -wie der Name schon suggeriert- sind ihre Elemente nicht geordnet.
ich dachte mir, falls ich da ins detail gehe, müsste ich
std::hash
erwähnen. soll ich?auf den rest gehe ich mal ein, wenn ich mehr zeit habe
aber danke für die außerordentlich konstruktive kritik.
-
queer_boy schrieb:
ich dachte mir, falls ich da ins detail gehe, müsste ich
std::hash
erwähnen. soll ich?Ich finde, dass man bei einer Hashtabelle die Hashfunktion erwähnen muss, denn ohne die geht da nix.
-
Folgender Link ist vielleicht ganz interessant: Implementierungsstatus C++200x der libstdc++
-
so, nun da ich endlich wieder etwas zeitlichen freiraum habe (und vor allem die nötige motivation gefasst), hab ich die Bens vorschläge eingearbeitet und gleich noch alles auf den neuesten stand gebracht (auch den anderen artikel).
wenn jetzt noch jemand vorschläge/wünsche hat, bitte äußern. ansonsten fehlen nur mehr die quellen zu den entsprechenden proposals und vielleicht ein paar weiterführende links.
btw. das beispiel für die RegExs habe ich - vereinfacht - aus der englischen wikipedia übernommen. im zuge der recherche bin ich allerdings drauf gekommen, dass die ziemlichen stumpfsinn geschrieben haben. naja, ist überarbeitet.
-
post scriptum:
ich habe jetzt übrigens auch endlich den ersten teil der serie in den wikipedia artikel zu C++ integriert. ich frage mich, ob es nicht vielleicht sinnvoll wäre, unter anderem um die bekanntheit des magazins zu fördern, auch andere artikel dort zu verlinken. z.b. scheint shades artikel über exceptions ganz gut zum entsprechenden wikipedia-artikel zu passen.
-
ready for cor[R]ections
-
So, ich bin glücklicherweise noch zur Korrektur gekommen; hoffe, dass ich in der Eile nichts übersehen habe.
Bezüglich Wikipedia: ich wollte den Artikel auch schon mal verlinken, aber damals war noch ne Vollsperrung über "C++" verhängt. Tja, und jetzt hat wohl wieder jemand den Link rausgelöscht. –.–
Begründung: „Der Überblick ist nicht umfassend. Verlässlichkeit des angegebenen Links ist auch unklar.“ Das Argument „nicht umfassend“ dürfte sich mit Erscheinen dieses Artikels erledigt haben, und viel verlässlicher als nach jedem Abschnitt die Quellen anzugeben geht ja wohl auch nicht.
Deshalb wäre eine erneute Verlinkung auf beide Teile imho gerechtfertigt.
Super Artikel übrigens.
—————C++09 (Teil 2) - Ein Überblick: Die Standardbibliothek
Dies ist der zweite Teil der Serie über den wahrscheinlich 2009 erscheinenden neuen Standard für C++.
Mit dem Technical Report (TR1) hat schon 2005 zuallererst die Standardbibliothek ein neues Gesicht bekommen, vor allem Boost diente dabei als hervorstechende Quelle der Inspiration. Der Technical Report 1 wird in C++09 vollständig enthalten sein, auch wenn teilweise Namen verändert und neue Funktionalitäten hinzugefügt werden.
Damit wird die Standardbibliothek von C++09 jener Bereich sein, in dem die meisten Neuerungen stattfinden werden. Der Bereich im C++-Standard, der ihr gewidmet ist hat sich allein von der Seitenanzahl her jetzt schon seit C++03 beinahe verdoppelt (von knapp unter 400 Seiten auf über 700 Seiten im aktuellen Working-Draft).
Der Hauptteil besteht dabei aus Erweiterungen aus dem TR1, ein weiterer großer Teil beschreibt die Unterstützung von paralleler Verarbeitung (Multithreading-Bibliothek und atomare Operationen). Ich möchte hier nicht allzu sehr ins Detail gehen, vor allem nicht Details erklären oder Schnittstellenspezifikationen aufschreiben, dafür gibt es ja den Standard selbst. In erster Linie möchte ich Beispiele geben, die zukünftige Möglichkeiten der Standardbibliothek aufzeigen.
Hinweis: Ich verwende in den Code-Beispielen bewusst exemplarisch neue Sprachfeatures und damit die teilweise recht ungewohnte Syntax von C++09. Dazu gehört vor allem:
- Die einheitliche Form der Initialisierung mit geschwungenen Klammern
- Variadic Templates
- Rvalue-Referenzen
- Die neue Form der Funktionsdeklaration
Für ein besseres Verständnis des Codes ist daher die Lektüre der entsprechenden Kapitel aus dem ersten Teil der Reihe (C++09 - Ein Überblick: Sprachfeatures) empfehlenswert.
Inhalt
- 1 Erweiterungen aus dem TR1
- 1.1 Utilities
- 1.2 Funktionsobjekte
- 1.3 Neue Container
- 1.4 Type-Traits
- 1.5 Reguläre Ausdrücke
- 1.6 Zufallszahlen
- 2 Multithreading
- 2.1 Threads
- 2.2 Mutexe und Monitore
- 3. Datum und Zeit
- 4 System-Fehler
- 5 Integration neuer Sprachfeatures
- 5.1 Standard-Concepts
- 5.2 Noch mehr:
std::string
- 6 Erweiterungen für den TR2 und danach
- 6.1 Filesystem
- 6.2 Weitere mögliche Neuerungen
- 7 Ausblick
- 8 Verweise
1 Erweiterungen aus dem TR1
Der Technical Report 1 ist ein Dokument, in dem verschiedene Erweiterungen der C++-Standardbibliothek vorgeschlagen werden. Der TR1 wurde 2005 veröffentlicht und viele der Funktionen werden von aktuellen Compilern (bzw. Standardbibliotheken) bereits unterstützt, teils auch bereits mit Hilfe der neuen Sprachfeatures.
1.1 Utilities
Der TR1 definiert einige nützliche Klassen, die das Leben in vielen Bereichen einfacher machen. Zu ihnen gehören
- Reference Wrapper. Diese sind nützlich, um Referenzen an Funktionstemplates zu übergeben, die eigentlich Kopien ihrer Argumente anfertigen würden.
- Smart Pointer. Zeiger, die sich selbst um die Freigabe des Speichers kümmern.
- Tuple. Ein Container fixer Länge, der Daten verschiedenen Typs aufnehmen kann.
Beispiel
#include <functional> //Reference-Wrapper #include <memory> //Smart-Pointer #include <tuple> //Tuple #include <vector> using namespace std; template <class T> void fun (T t) { t += 21; } int main () { int i = 21; fun (ref(i)); //Einen Reference-Wrapper erstellen //i == 42 auto p = make_shared<int>(42); //new int(42) für Smart-Pointer *p = 23; //p verhält sich wie ein Zeiger. //Nur: das delete geschieht automatisch. //Dynamischen std::vector mit Inhalt 1, 2, 3, 4 erzeugen: auto vi = make_shared<vector<int>>({1,2,3,4}); //Beliebig viele verschiedene Typen in einem Tupel speichern: auto foo = make_tuple(5, 4.2, 2.3); auto bar = make_tuple("std::string"s, 'c'); //Zwei tuple-Instanzen vereinigen auto fubar = concatenate(foo, bar); //Werte auslesen auto first = get<1>(foo); //decltype(first) -> int auto second = get<2>(foo); //decltype(second) -> double } //Funktion, die mehrere Rückgabewerte hat: auto fun () -> tuple<int, vector<int>, string> { return make_tuple(5, { 5,4,3,2,1 }, "foobar"s); }
Erläuterung
Wie der Header<functional>
schon vermuten lässt, werden Reference-Wrapper hauptsächlich in Verbindung mit Funktionen höherer Ordnung verwendet. Ein weiteres Beispiel hierzu findet sich in 1.2.Smart-Pointer sind schon eine Weile im Umlauf. Der Standard von 2003 bot bereits einen:
std::auto_ptr
. Dieser hatte jedoch nicht immer die Semantik, die man erwartet hätte (Move-Semantik statt Kopiersemantik). Sein größter Nachteil war, dass er nicht in Standard-Containern verwendet werden konnte.std::auto_ptr
ist mit C++09 deprecated und kann durchstd::shared_ptr
bzw.std::unique_ptr
ersetzt werden, je nach Aufgabe.std::tuple
ist dazu gedacht,std::pair
zu ersetzen, denn es kann nicht nur zwei sondern beliebig viele Werte unterschiedlichen Typs aufnehmen.std::pair
wird mit C++09 ebenfalls deprecated sein.Status
Der TR1 ist Teil des aktuellen Working Drafts des kommenden C++-Standards.Unterstützung
Der TR1 wird von allen größeren Standardbibliothek-Implementierungen unterstützt. Da der TR1 verbietet, dass seine Klassen und Funktionen direkt in den Standardheadern eingebaut werden, muss man diese zumeist explizit aktivieren (entweder über ein Makro oder durch einen anderen Include-Pfad, bspw.<tr1/functional>
. Zu beachten ist auch, dass bestimmte Funktionen wie etwamake_shared
nicht im TR1 enthalten ist, da es auf Rvalue-Referenzen basiert (siehe Teil 1 der Reihe). Zumindest die libstdc++ implementiert in Version 4.3 einige Funktionalität mithilfe der neuen Sprachfeatures (Dazu muss man beim Kompilieren-std=c++0x
aktivieren). Allerdings gibt es keine vollständige Implementierung des TR1.Proposals
N1403 - Tuple
N1450 - Smart-Pointer
N1453 - Reference-Wrapper
N1836 - Der Draft des TR11.2 Funktionsobjekte
Bereits C++03 bot einige Funktionen höherer Ordnung an, das sind Funktionen, die andere Funktionen als Argument erwarten und mit ihnen umgehen. Die Verwendung von ihnen war jedoch oft nur eingeschränkt möglich. Mit
std::function
gibt es nun einen polymorphen Funktionswrapper, der auch Zeiger auf Memberfunktionen von Klassen beinhalten kann. Außerdem wurde die Beschränkung vonstd::bind1st
undstd::bind2nd
, Argumente jeweils nur an den ersten oder zweiten Parameter binden zu können zu Gunsten vonstd::bind
, das mit Funktionen mit unbestimmter Anzahl an Parametern arbeitet, aufgegeben.Beispiel
#include <functional> //Binder, Funktionsobjekte #include <algorithm> using namespace std; //Addiert eine Zahl zu einer anderen: template <typename T> int accumulate (T value, T& acc) { return acc += value; } //Addiert zwei Zahlen und zählt mit, wie oft es aufgerufen wurde template <class T> struct Adder { int count; Adder () : count(0) {} T add (T a, T b) { ++count; return a + b; } }; int main () { vector<int> v1 { 1,2,3 }; int value = 0; for_each (v1.begin(), v1.end(), bind(accumulate<int>, placeholders::_1, ref(value))); //value == 1 + 2 + 3 //Mache aus der Function "accumulate" eine neue Funktion, die eine Zahl inkrementiert: function<int(int&)> incrementor = bind(accumulate<int>, 1, placeholders::_1); incrementor(value); //value == 7 //Auch Elementfunktion können verwendet werden: Adder<int> x; //Addiert 5 zu jedem Element transform (v1.begin(), v1.end(), v1.begin(), bind(&Adder<int>::add, &x, placeholders::_1, 5)); //x.count == 3 //v = { 6, 7, 8 } }
Erläuterung
std::bind
ist dazu gedacht, die Standardfunktionenstd::bind1st
undstd::bind2nd
zu ersetzen. Um dies zu erreichen, werden "Platzhalter" eingeführt. Aus einer Funktion, die also eigentlich zwei Argumente benötigt, kann auf diese Art und Weise eine unäre Funktion gemacht werden:void foo_binary (int, int); function<void (int)> foo_unary = bind (foo_binary, 42, placeholders::_1); foo_unary (23); //gleichbedeutend mit: foo_binary (42, 23); function<void (int)> foo_reversed = bind (foo_binary, placeholders::_2, placeholders::_1); foo_reversed (1, 2); //gleichbedeutend mit: foo_binary (2, 1);
Erläuterung
Da C++09 jedoch direkt Closures (Lambda-Funktionen) unterstützt, werden viele Bereiche, in denen momentan boost.bind eingesetzt wird, durch diese übernommen. So lässt sich obiges Beispiel mit Closures auf folgende Art und Weise implementieren:#include <functional> //Binder, Funktionsobjekte, std::reference_closure #include <algorithm> using namespace std; int main () { vector<int> v1 { 1,2,3 }; int value = 0; for_each (v1.begin(), v1.end(), [&value](int element) (value += element)); //value == 1 + 2 + 3 //Speichere ein (Reference-)Closure reference_closure accumulate = [](int value, int& acc) -> int { return acc += next; }; //Mache aus dem Closure "accumulate" eine neue Funktion, die eine Zahl inkrementiert: function<int(int&)> incrementor = bind(accumulate, /* inkrementiere um */ 1, placeholders::_1); incrementor(value); //value == 7 }
Erläuterung
Dieses Beispiel zeigt, dass sich mit Closures viele Gebiete, die derzeit von umständlich zu erstellenden Funktionsobjekten abgedeckt werden, einfacher implementieren lassen.Status
siehe 1.1Unterstützung
siehe 1.1
Closures und damit auchstd::reference_closure
werden allerdings noch von keinem Compiler unterstützt.Proposals
N1402 - Polymorphe Funktionsobjekt-Wrapper
N1836 - Der Draft des TR11.3 Neue Container
TR1 definiert drei neue Container, die dieselbe Schnittstelle wie die anderen Standardcontainer anbieten. Es sind dies:
std::array
für nicht-dynamische Arrays fixer Größestd::unordered_set
, die gehashte Version vonstd::set
std::unordered_map
, die gehashte Version vonstd::map
- C++09 enthält außerdem noch einen zusätzlichen Container,
std::forward_list
Beispiel
#include <array> #include <unordered_map> #include <unordered_set> #include <forward_list> int main () { std::array<int, 20> array; std::unordered_map<string, int> unordered_map; std::unordered_set<double> unordered_set; std::forward_list<int> li = { 1, 2, 3, 4 }; }
Erläuterung
std::unordered_map
undstd::unordered_set
lassen sich – vom Interface her – ebenso verwenden wie die Standardcontainerstd::map
undstd::set
. Statt einen Vergleich mit dem Kleiner-als-Operator durchzuführen (standardmäßiges Verhalten beistd::map
undstd::set
), wird hier eine Hash-Funktion verwendet. Um mit benutzerdefinierten Typen arbeiten zu können, muss daher eine Spezialisierung vonstd::hash
(Header<hash>
) implementiert werden.
std::forward_list
ist eine einfach verkettete Liste, im Gegensatz zustd::list
, die doppelt verkettet ist.Status
siehe 1.1Unterstützung
siehe 1.1Proposals
N1456 - Hashtables
N1548 - Array
N1836 - Der Draft des TR1
N2545 - Singly-linked list1.4 Type-Traits
Type-Traits sind ein beliebtes Mittel der Template-Metaprogrammierung, das hilft, generischen Typen bestimmte Constraints zu setzen. Ob und wie weit sich dies durch den Einsatz von Konzepten (siehe hierzu Teil 1 der Reihe) ersetzen lässt, wird sich zeigen.
Beispiel
#include <type_traits> using namespace std; template <typename T, class Base, class Derived> struct Foo { static_assert (is_integral<T>::value, "T ist nicht integral!"); static_assert (is_base_of<Base, Derived>::value, "Erwarte voneinander abgeleitete Typen!"); }; template <typename T> void foo (T const& t, typename enable_if<is_pod<T>>::type * = 0) { } struct Virtual { virtual ~Virtual () = default; }; int main () { //OK, 5 ist plain-old-data foo(5); //Ups, polymorphe Klasse ist kein POD - Kompilierfehler foo(Virtual()); }
Erläuterung
Es stellt sich bei Type-Traits natürlich die Frage, inwieweit sie durch Konzepte (siehe Teil 1 der Reihe) abgelöst werden. Diese bieten ein eleganteres Interface, um Template-Parametern gewisse Constraints aufzuerlegen.Status
siehe 1.1
Im TR1 allerdings nicht enthalten sindstd::enable_if
,std::conditional
undstd::decay
. Diese sind allerdings im Working-Draft des kommenden C++-Standards beinhaltet.Unterstützung
Die im TR1 nicht enthaltenen Type-Traits befinden sich auch nicht im Namensbereichstd::tr1
. Als Alternative lassen sich bisweilen die entsprechenden Type-Traits aus der Boost-Bibliothek verwenden. Fürstd::decay
gibt es allerdings meines Wissens nach noch keine Implementierung.Proposals
N1424 - Type Traits
N1836 - Der Draft des TR1
N2240 -std::enable_if
undstd::conditional
N2244 -std::decay
1.5 Reguläre Ausdrücke
Reguläre Ausdrücke oder "regular expressions" (RegExp, RegEx) sind Zeichenketten, die eine Art Filterkriterium für Texte darstellen. So ist es möglich mit einem regulären Ausdruck einen Text zu beschreiben, der mit einem "A" beginnt, nur aus Buchstaben besteht und mit einem Punkt endet. Einsätze sind dabei vor allem komplizierte Suchausdrücke, aber auch Ersetzungen, indem man die zu suchenden Textteile als reguläre Ausdrücke definiert. Reguläre Ausdrücke sind in vielen Programmiersprachen gang und gäbe.
Beispiel
#include <regex> #include <iostream> #include <string> using namespace std; int main () { char const* text = "Ein langer Text\tmit Trennzeichen; Tja"; smatch matches; //typedef für match_results<string::const_iterator> //Splitte Text nach Trennzeichen if (regex_search(text, matches, regex("[ ,.\\t\\n;:]")) { for (auto match: matches) //decltype(match) -> std::sub_match<std::string::iterator> bzw. std::ssub_match { cout << match << '\n'; } } }
Erläuterung
Ein regulärer Ausdruck kann auf verschiedene Art und Weisen implementiert sein. Die Standardbibliothek liefert die Möglichkeit, zu testen, welche Art von regulären Ausdrücken sie unterstützt, dieses Feature wurde im Beispiel der Einfachheit halber jedoch nicht verwendet. Die Klassestd::regex
bzw.std::basic_regex<>
dient dafür, die Engine, die im Hintergrund Strings parst, zu abstrahieren. Mithilfe verschiedener Standardfunktionen wiestd::regex_search
oderstd::regex_replace
lässt sich so mit regulären Ausdrücken arbeiten.Ein eigener Container, der Matches für reguläre Expressions aufnehmen kann, ist dabei
std::match_results
, bzw. die fürstd::string
explizit spezialisierte Versionstd::smatch
. Dastd::match_results
einen Container darstellt, ist es möglich, mittels Iteratoren einzelne Matches (std::sub_match
bzw. fürstd::strings
std::ssub_match
) auszulesen. Dieses ist im Prinzip einfach vonstd::pair<Iterator, Iterator>
abgeleitet, besitzt aber einen Konvertierungsoperator nachstd::string
.Ein weiteres Feature von regulären Ausdrücken in C++09 ist, dass es auch mit den C++-Locales zusammenarbeitet. Eine eigene Regex-Traits-Klasse (
std::regex_traits
), die für einzelne reguläre Ausdrücke individualisiert werden kann, sorgt dabei für die Anbindung an bestimmte Locales – dabei ist anzumerken, dass eigene Spezialisierungen vonstd::regex_traits
auch non-C++-Locales verwenden können. Regex-Traits erlaubt außerdem, die Syntax von regulären Ausdrücken den eigenen Gewohnheiten anzupassen.Alles in allem stellt die C++09-RegEx-Bibliothek ein mächtiges Werkzeug dar, um mit regulären Ausdrücken auch international arbeiten zu können.
Status
siehe 1.1Unterstützung
Die C++09-RegEx-Bibliothek wird, obwohl sie bereits im TR1 enthalten war, meines Wissens noch von keinem Compiler unterstützt.Proposals
N1429 - Reguläre Ausdrücke
N1836 - Der Draft des TR11.6 Zufallszahlen
Der TR1 definiert ein mächtiges Interface, um Zufallszahlen zu erzeugen. Er definiert dabei sogenannte "Engines", die Zufallszahlen nach bewährten Algorithmen erzeugen und stellt diverse Verteilungen zur Verfügung, die mit diesen Engines arbeiten können.
Beispiel
#include <random> using namespace std; int main () { //Zufallszahlen-Seeder: Erwartet in dem Fall Eingaben von std::cin seed_seq seeder {istream_iterator<int>(cin), istream_iterator<int>()}; mt19937 mersenne_twister_engine {seeder}; ranlux24 discard_block_engine {seeder}; knuth_b shuffle_order_engine {seeder}; bernoulli_distribution distribution_a {0.25}; uniform_int_distribution<int> distribution_b {0, 99}; weibull_distribution<double> distribution_c; bool a = distribution_a (discard_block_engine); int b = distribution_b (shuffle_order_engine); double c = distribution_c (mersenne_twister_engine); }
Status
siehe 1.1Unterstützung
Die tatsächlichen Bezeichnungen weichen zum Teil vom Vorschlag im TR1 ab. Außerdem wird meines Wissens nach die Zufallszahlen-Bibliothek von C++09 noch von keinem Compiler zu 100% unterstützt.Proposals
N1836 - Der Draft des TR1
N2111 - Die Zufallszahlen-Bibliothek2 Multithreading
C++09 bietet den langersehnten Multithreading-Support. Dies geschieht einerseits über die Multithreading-Bibliothek, die Objekte für Threads, Mutexe und Locks und Monitore (Condition Variables) bereitstellt.
2.1 Threads
In Zeiten, in denen Prozessoren mehrere Kerne haben, ist es sinnvoll, Berechnungen auf diese zu verteilen. Auch Programme auf Single-Core-Prozessoren können durch den Einsatz von Threads verbessert werden, indem einzelne Komponenten des Programms voneinander entkoppelt jeweils den eigenen Anforderungen entsprechend Systemzeit verbrauchen können.
Beispiel 1 - Neue Threads erstellen
#include <thread> #include <functional> #include <algorithm> #include <iostream> #include <iterator> #include <vector> void bar () { } struct MyThread { void start (int data) { //An den nächsten Thread weitergeben this_thread::yield(); } }; int main () { //Starte neuen Thread mit Aufruf von bar(); std::thread thread_a {bar}; MyThread instance; //Starte neuen Thread mit Aufruf von instance.start(42); std::thread thread_b {std::mem_fun(&MyThread::start), &instance, 42}; std::vector<int> data; //Kopiere in einem neuen Thread Daten von cin in den vector std::thread reader ( [&]() ( std::copy(std::istream_iterator<int>(std::cin), std::istream_iterator<int>(), std::back_inserter(data)) ) ); //Warte auf den Eingabe-Thread reader.join(); //Gib aus, was die Hardware an Parallelität verträgt std::cout << std::thread::hardware_concurrency() << std::endl; }
Erläuterung
Dastd::thread
mithilfe von Variadic Templates implementiert ist, können beliebige Funktionen und Funktionsobjekte als Eingangspunkte für einen Thread definiert werden. Sowohl freistehende Funktionen wiebar
als auch Elementfunktionen wieMyThread::start
und sogar Lambda-Funktionsobjekte (siehe dazu den ersten Teil der Reihe) können als eigener Thread ausgeführt werden. Das Interface vonstd::thread
erinnert ein bisschen an die entsprechende Boost-Bibliothek.Beispiel 2 - Einmalige Initialisierung
#include <thread> #include <array> std::once_flag init_once; void general_init_routine (int data) { std::cout << "Führe allgemeine Initialisierungsaufgaben (Singletons?) durch\n"; } void thread_fun (int x) { std::call_once (init_once, general_init_routine, x); //general_init_routine wurde mit Sicherheit ausgeführt und beendet. } int main () { array<thread, 3> threads = { thread {thread_fun, 1}, thread {thread_fun, 2}, thread {thread_fun, 3} }; //Warte auf alle Threads for (auto thread: threads) thread.join(); }
Erläuterung
In diesem Beispiel werden drei Threads erstellt, die jeweils versuchen, eine gemeinsame Initialisierungsfunktion aufzurufen. Mithilfe vonstd::once_flag
undstd::call_once
ist sichergestellt, dass diese Funktion tatsächlich nur ein einziges Mal aufgerufen wird. Alle anderen Threads müssen mit ihrer Ausführung außerdem warten, bis diese Funktion auch tatsächlich aufgerufen wurde. In diesem Beispiel könntegeneral_init_routine
also entweder mit 1, 2 oder 3 aufgerufen werden.Status
Die Multithreading-Bibliothek ist Teil des aktuellen Working-Drafts.Unterstützung
Die Boost-Bibliothek Boost.Threads ist ähnlich aufgebaut, jedoch nicht vollständig kompatibel mit der C++09-Multithreading-Bibliothek. Dastd::thread
sehr stark auf neue Sprachfeatures baut, und diese nur von wenigen Compilern (in der Form nur von GCC 4.3) unterstützt werden, ist derzeit keine Implementierung verfügbar.Proposals
N2139 - Hintergründe
N2410 - Thread-Safety in der Standardbibliothek
N2427 - Atomare Operationen
N2480 - Eine informelle Erklärung des neuen C++-Speichermodells
N2497 - Die Multithreading-Bibliothek2.2 Mutexe und Monitore
Mutexe und Monitore dienen der sicheren Verwendung von Daten zwischen mehreren Threads. Sie verhindern Wettlaufsituationen, das sind Situationen, in denen das Ergebnis einer Operation vom zeitlichen Verhalten bestimmter Einzelsituationen abhängt. Ein sicherer Umgang mit ihnen verhindert außerdem Deadlocks, das sind Situationen, in denen Threads auf Informationen warten, die sie jedoch erst durch einen jeweils anderen Thread bekommen würden. Da dieser andere Thread jedoch selbst auf Informationen wartet (zyklische Abhängigkeit), stoppt das Programm.
Beispiel 1 - Mutexe und Locks
#include <thread> #include <mutex> #include <iostream> #include <array> #include <string> std::mutex input_mutex, output_mutex; void thread_fun () { for (;;) { std::string txt; { std::lock_guard<std::mutex> lock {input_mutex}; std::cin >> txt; } { std::lock_guard<std::mutex> lock {output_mutex}; std::cout << txt << '\n'; } } } int main () { std::array<std::thread, 2> threads = { std::thread {thread_fun}, std::thread {thread_fun} }; for (auto thread: threads) thread.join(); }
Erläuterung
Mutexe ermöglichen die sichere Verwendung von Objekten, auf die mehrere Threads gleichzeitig zugreifen wollen. Dazu bieten sie die Funktionenlock
,try_lock
undunlock
an. Eine bestimmte Art von Mutexen,std::timed_mutex
undstd::recursive_timed_mutex
bieten zusätzlich die Möglichkeit, die Ressource nur für eine bestimmte Zeitspanne zu blockieren.
Das automatische Sperren und entsperren eines Mutex (RAII) geschieht mit Hilfe der beiden Lock-Klassenstd::lock_guard
undstd::unique_lock
, wobeistd::unique_lock
am ehesten demboost::scoped_locked
entspricht.
Mit den Standard-Funktionenstd::try_lock
undstd::lock
ist es zudem möglich, eine beliebige Anzahl von Locks auf eine sichere Weise gleichzeitig zu sperren.Beispiel 2 - Monitore
#include <thread> #include <mutex> #include <condition_variable> #include <queue> #include <iostream> std::mutex guard; std::condition_variable monitor; std::queue<int> data; void produce_data () { for (;;) { int x; std::cin >> x; //Threadsicheres Speichern der neuen Daten std::lock_guard<std::mutex> lock {guard}; data.push_back(x); //Signalisiere, dass neue Daten da sind if (data.size()) monitor.notify_one(); } } void consume_data () { for (;;) { int x; { //Versuche, Daten zu lesen std::unique_lock<std::mutex> lock {guard}; //Warte, bis neue Daten ankommen monitor.wait(lock, [&]()(!data.empty())); //Daten sind verfügbar x = data.front(); data.pop(); } std::cout << "Eingabe: " << x << std::endl; } } int main () { std::thread producer {produce_data}; std::thread consumer {consume_data}; producer.join(); consumer.join(); }
Erläuterung
Der Namecondition_variable
rührt daher, dass eine bestimmte Bedingung erfüllt sein muss, bis ein Thread seine Arbeit wieder aufnimmt: Diese Bedingung stellt in obigem Beispiel die Lambda-Funktion[&]()(!data.empty())
dar. Der aktuelle Thread soll also solange warten, bisdata.empty()
nicht mehrtrue
liefert, also solange bis neue Daten eingetroffen sind. Diese Benachrichtigung erhält er durch den anderen Thread (monitor.notify_one()
).
wait
entsperrt gleichzeitig den Lock, so dass der Producer-Thread diequeue
mit neuen Daten füllen kann. Sobald die Bedingung fürwait
erfüllt ist, wird der Lock allerdings wieder hergestellt.Status
Siehe 2.1Unterstützung
Siehe 2.1Proposals
N2406 - Hintergründe zu Mutexen, Locks und Monitoren3 Datum und Zeit
Da die C++-Multithreading-Bibliothek auch Funktionen wie Sleep und zeitabhängiges Warten auf Ressourcen ermöglichen soll, musste für sie eine eigene Date-Time-Bibliothek geschaffen werden.
Die meisten Funktionen sind bereits in der Boost Date-Time-Bibliothek implementiert. Die Date-Time-Bibliothek, die in C++09 inkludiert werden soll, sieht sich dabei als Vorgängerin für eine noch umfassendere Bibliothek im Technical Report 2.Beispiel 1 - Date-Time in Verbindung mit Threads
#include <date_time> #include <thread> template <class Lock> void thread (Lock lock) { std::this_thread::sleep(std::seconds(1)); std::recursive_timed_mutex rtm; rtm.time_lock(std::milliseconds(20)); }
Erläuterung
Die Date-Time-Bibliothek führt drei Arten von Typen ein:- Zeitpunkte. Ein Zeitpunkt repräsentiert einen Moment im Zeitkontinuum.
- Dauer. Eine Dauer ist nicht von einem Zeitpunkt abhängig und repräsentiert eine gewisse Zeitlänge. Diese kann auch negativ sein.
- Uhren. Ein Uhren-Typ ist ein Interface zu einem Gerät, das einen Zeitpunkt zurückgibt.
Die konkreten Typen in C++09 sollen sein:
- utc_time - ein Zeitpunkt in einer Auflösung von Nanosekunden.
- hiresolution_clock - Eine Uhr, die die momentane utc_time zurückgibt.
- hours, minutes, seconds, milliseconds, microseconds und nanoseconds - Typen, die eine Zeitdauer repräsentieren.
Mit diesen Typen kann man intuitiv arbeiten.
Beispiel 2 - Intuitives Interface
#include <date_time> using namespace std; int main () { utc_time now = hiresolution_clock::universal_time(); utc_time tomorrow = now + hours(24); hours aday {24}; aday += minutes {10};//Kompilierfehler: Verlust an Genauigkeit minutes m {10}; m += aday; //OK, Stunden können in Minuten umgerechnet werden }
Erläuterung
Ein Objekt vom Typutc_time
, das von seinem Standardkonstruktor initialisiert wird, repräsentiert die UTC-Zeit 1970-01-01 00:00:00.000000000.Status
Die Date-Time-Library wurde gleichzeitig mit der Multithreading-Bibliothek in den Working-Draft aufgenommen. Allerdings wurde sie mittlerweile wieder aus dem Working-Draft entfernt, da zu viele Details nicht geklärt werden konnten. Eine Erklärung für ihr Verschwinden findet sich hier.Unterstützung
Mir sind keine Compiler bekannt, die diese Features unterstützen.Proposals
N2411 - Date/Time für C++0x
N2492 - Date/Time als Teil der Multithreading-Bibliothek
N2498 - Custom Time Duration Support4 System-Fehler
Vor allem im Hinblick auf die Dateisystem-Bibliothek, die mit dem Technical Report 2 eingeführt werden soll, ist es nötig, Fehlermeldungen des Systems in C++-Ausnahmen zu verwenden. Aber auch die Multithreading-Bibliothek profitiert davon, System-Fehlermeldungen über eine standardisierte Schnittstelle zu präsentieren. Der Standard stützt sich dabei auf die POSIX-Fehlerflags.
*Beispiel 1 - Verwendung von
system-error
*#include <system_error> #include <thread> #include <iostream> void foo () {} int main () { try { std::thread thr { foo }; } //Wenn der Thread nicht gestartet werden konnte... catch (std::system_error& error) { std::cerr << error.what(); } }
Beispiel 2 - Eigene Erweiterungen
#include <system_error> namespace std { struct my_error_catalog : virtual public std::error_catalog { typedef std::error_catalog base_type; static value_type const out_of_ink = 20001; static value_type const out_of_paper = 20002; virtual auto is_valid_value (value_type v) const throw () -> bool { if (!base_type::is_valid_value(v)) return v == out_of_ink || v == out_of_paper; return true; } virtual auto str (value_type v) const throw () -> char const* { char const* ink = "Out of Ink"; char const* paper = "Out of Paper"; if (base_type::is_valid_value(v)) return base_type::str(v); switch (v) { case out_of_ink: return ink; case out_of_paper: return paper; default: return nullptr; } } virtual auto last_value() const throw() -> value_type const { return out_of_paper; } } } //... if (printer.ink_level() < 5percent) { throw system_error { std::my_error_catalog::out_of_ink, my_error_catalog() }; }
Erläuterung
Eine der Stärken der System-Error Schnittstelle ist es,std::locales
zu unterstützen. Die Vorgabe lautet außerdem, dass die Nachrichten, diewhat
liefert, eine Fehler-Diagnose erleichtern sollen.Status
Dieses Feature ist Teil des Working-Drafts. Allerdings gibt es bereits Vorschläge, die Schnittstelle zu modifizieren bzw. zu erweitern. So wie es aussieht sind auch hier einige Unklarheiten aufgetreten, und es existiert bereits der Vorschlag,system_error
wieder aus dem Working-Draft zu entfernen, wie bereits mit der Date/Time-Bibliothek geschehen.Unterstützung
Mir sind keine Compiler bekannt, die dieses Feature unterstützen.Proposals
N2303 - System-Error-Unterstützung
N2538 - Entfernen der System-Error-Unterstützung5 Integration neuer Sprachfeatures
Durch die vielen neuen Möglichkeiten, die C++09 bietet – von Variadic Templates, Rvalue-Referenzen bis zur Unicode-Unterstützung – war es ebenso nötig, bereits bestehende Bibliotheken dem Wandel der Sprache anzupassen. Neben der Einführung von Konzepten und Concept-Maps für die Klassen der STL und der Implementierung von Move-Semantik durch Rvalue-Referenzen, die weitgehend unsichtbar im Hintergrund arbeitet, gibt es auch einige durchaus sichtbare und erwähnenswerte Features, die das Arbeiten mit der "alten" Standardbibliothek erleichtern sollen.
5.1 Standard-Concepts
Um das Rad nicht ständig neu erfinden zu müssen, definiert die Standard-Bibliothek einige Konzepte wie LessThanComparable, die häufig gebraucht werden. Waren es einst bestimmte Bedingungen, die nur formal an Konzepte wie Iteratoren, Container usw. gestellt wurden, sind diese nun auch explizit im Code formulierbar.
Beispiel
#include <concepts> struct MyOrder { int i; }; bool operator == (MyOrder const& a, MyOrder const& b) { return a.i == b.i; } bool operator < (MyOrder const& a, MyOrder const& b) { return a.i < b.i; } template <typename T> requires EqualityComparable<T> && LessThanComparable<T> void foo (T a, T b) { a == b; a != b; //automatisch generiert - Definition in EqualityComparable a >= b; //automatisch generiert - Definition in LessThanComparable } struct MyInteger { long long value; MyInteger (long long v) : value(v) {} MyInteger& operator+= (MyInteger x) { value += x.value; return *this; } //... }; template <Arithmetic T> void bar () { T a{1}, b{2}, c{3}; a = b + c; //operator+ wird automatisch erzeugt. }
Status
Daconcepts
selbst noch nicht im Working-Draft enthalten sind, ist auch der Header<concepts>
noch nicht mit dabei. Bis zur Veröffentlichung des neuen Standards wird sich dies jedoch mit Sicherheit noch ändern.Unterstützung
ConceptGCC unterstützt die<concepts>
-Bibliothek. Allerdings kann ConceptGCC noch nicht sogenannte "Default-Implementierungen" ausconcepts
erstellen. (D.h. die obigen Beispiele funktionieren nicht.)Proposals
N2036 - Hintergründe
N2037 - Concepts für die Standardbibliothek (Einführung)
N2572 - Die wichtigsten Concepts5.2 Noch mehr: std::string
Die Funktionalität rund um
std::string
wird noch weiter ausgebaut, um ihn endgültig zur Standard-Stringklasse für C++ zu machen und von den lästigen nullterminierten Char-Zeigern wegzukommen.*Beispiel 1 - Schnittstellen für
std::string
*#include <locale> #include <fstream> #include <string> int main () { std::string loc { "fr_FR.UTF-8" }; std::locale myLocale { loc }; //Statt loc.c_str() std::string filename { "foo.txt" }; std::fstream file { filename };//Dito hier file.close(); //Auch filebufs unterstützen jetzt std::strings file.rdbuf()->open(filename, ios::out); }
Erläuterung
Mit diesem Feature wird das Interface der Standardbibliothek vereinheitlicht und anfängerfreundlicher gestaltet.*Beispiel 2 -
operator<<
*#include <string> int main () { std::string a, b { "strings" }; a << "Aneinander" << 'r' << "eihen von " << b; }
Erläuterung
Das Konkatenieren von Strings wird somit übersichtlicher als die Alternative mitoperator+=
. Allerdings wirdoperator<<
nicht die Konversion von anderen Typen nachstd::string
ermöglichen.Beispiel 3 - neue Stringfunktionen
#include <stdexcept> #include <iostream> #include <string> int main () { std::string number; cin >> number; try { long twice = std::stol(number) * 2; double half = std::stod(number) / 2; string output = "Zweimal " + number + " ist " + to_string (twice) + ", die Hälfte davon ist " + to_string(half); } catch (std::invalid_argument const&) { //Konnte number nicht in long oder double konvertieren } catch (std::out_of_range const&) { //Number war zu groß/klein für long oder double } }
Erläuterung
Mit den Standardfunktionenstd::stoi
,std::stol
,std::stoul
,std::stoll
,std::stoull
,std::stof
,std::stod
,std::stold
undstd::to_string
wird endlich "von Zahl nach String und zurück" ohne Stringstreams ermöglicht.Status
Die neuen Schnittstellen fürstd::strings
sind bereits im Working-Draft enthalten. Andere Erweiterungen, die von Neuerungen in der Sprache selbst abhängen, werden frühestens dann inkludiert, wenn das entsprechende Feature in den Working-Draft kommt.operator<<
ist leider noch nicht im Working-Draft und es ist nicht sicher, ob er es noch rechtzeitig in den Standard schafft. Die numerischen Konvertierungsfunktionen sind bereits Teil des Working-Drafts.Proposals
N1981 - Einheitliche Verwendung vonstd::string
N2233 -basic_string operator<<
6 Erweiterungen für den TR2 und danach
Während noch fleißig am kommenden Standard gearbeitet wird, lässt sich auch schon ein bisschen was über den Technical Report 2 sagen: Unterstützung für Dateisystem-Operationen wird definitiv darin enthalten sein. Anderes, wie eine Netzwerkbibliothek, befindet sich noch in einer sehr frühen Planungsphase, auch wenn das Komitee den Neuerungen gegenüber recht aufgeschlossen ist. Das liegt wohl auch hauptsächlich daran, dass die meiste Arbeitszeit nun in den kommenden Standard fließt und nicht in spekulative Erweiterungen danach.
6.1 Dateisystem
Die Dateisystem-Bibliothek, die in den Technical-Report 2 aufgenommen werden soll, orientiert sich stark an Boost.Filesystem.
Beispiel
#include <iostream> #include <filesystem> using std::tr2::sys; using std::cout; int main (int argc, char** argv) { std::string p { argc <= 1 ? "." : argv[1] }; if (is_directory(p)) { for (directory_iterator iter(p); iter != directory_iterator(); ++iter) { cout << iter->path().leaf() << ' '; //Dateinamen anzeigen if (is_regular(iter->status())) cout << file_size(iter->path()); cout << '\n'; } } else cout << (exists(p) ? "Found: " : "Not Found: ") << p << '\n'; }
Status
Dieses Feature ist bereits als Teil des TR2 angenommen worden. Der TR2 steht allerdings noch weit vor der Veröffentlichung.Unterstützung
Mir sind keine Compiler bekannt, die dieses Feature unterstützen. Allerdings funktioniert Boost.Filesystem ähnlich.Proposals
N1975 - Quelle und Erläuterung des Beispiels6.2 Weitere mögliche Neuerungen
Über weitere Bibliotheken, die in den TR2 aufgenommen werden sollen, lassen sich im Moment nur Vermutungen aufstellen. Möglicherweise wird die Boost.Asio-Bibliothek für C++-Netzwerkfähigkeiten zuständig sein, Boost.Any könnte ebenso aufgenommen werden.
Sollten Ihnen als LeserInnen Bibliotheken einfallen, die Sie für besonders aufnahmenswert empfinden, folgen Sie dem "Call for Proposals" - Wer weiß, vielleicht kommt die eine oder andere Idee auch durch :).
Proposals
N2175 - Proposal für eine Netzwerkbibliothek
N1393 - Any Library Proposal
N2044 - Shared Memory Proposal
N2276 - Thread Pools und Futures7 Ausblick
In den folgenden Artikeln dieser Reihe beschäftige ich mich eingehender mit bestimmten Features, zu denen ich nun einen groben Überblick gegeben habe. Die Auswahl, nach der ich vorgehe, ist dabei einfach:
Bereits verfügbare Features zuerst, bevorzugt Änderungen, die einen großen Einfluss auf den zukünftigen Programmierstil haben werden.
Die Wahl fällt damit zunächst auf Variadic Templates, weil diese das nächste große Feature sind, das bereits implementiert ist und weil es wahrscheinlich ist, dass es in naher Zukunft von mehreren Compilern unterstützt wird, da es nicht sonderlich schwer zu implementieren ist.
8 Verweise
ISO - C++ Library Working Group (LWG) Status Report
Implementierungsstatus der libstdc++
C++0X - The New Face of Standard C++
Daraus: Minimal Garbage Collection Support - The Latest Proposal (I-III)
N2151 - Variadic Templates für die Standardbibliothek
-
michba schrieb:
So, ich bin glücklicherweise noch zur Korrektur gekommen; hoffe, dass ich in der Eile nichts übersehen habe.
super , gut dass das noch rechtzeitig geschafft ist; wenn ich richtig sehe ist der artikel dann der einzige für juni?
Bezüglich Wikipedia: ich wollte den Artikel auch schon mal verlinken, aber damals war noch ne Vollsperrung über "C++" verhängt. Tja, und jetzt hat wohl wieder jemand den Link rausgelöscht. –.–
Begründung: „Der Überblick ist nicht umfassend. Verlässlichkeit des angegebenen Links ist auch unklar.“ Das Argument „nicht umfassend“ dürfte sich mit Erscheinen dieses Artikels erledigt haben, und viel verlässlicher als nach jedem Abschnitt die Quellen anzugeben geht ja wohl auch nicht.
Deshalb wäre eine erneute Verlinkung auf beide Teile imho gerechtfertigt.
Super Artikel übrigens.tatsächlich wieder rausgelöscht... also was noch umfassender sein soll, weiß ich auch nicht. ein link auf den working draft? super informativ
und mit verlässlichkeit wahrscheinlich gemeint, dass meine wahre identität wohl ein geheimnis ist. tja...
-
mann, jetzt sehe ich mir doch tatsächlich nochmal den artikel zu C++ in der wikipedia genauer an, und ich glaube es kaum: der typ, der den link rausgelöscht hat, fügt ja absichtlich falsche sachen ein und beruft sich dabei noch auf eine (veraltete) quelle...
-
wie ich gerade gehört habe, soll der neue standard nach samstag feature-complete werden (es läuft gerade ein meeting in frankreich) - womöglich muss ich also die beiden artikel in nächster zeit nochmal überarbeiten. meine frage nun: soll ich die änderungen einfach selbst einfügen, auch wenn der artikel schon veröffentlicht wurde, oder soll ich die artikel hier dann nochmal posten, um sie korrektur lesen zu lassen? (so wie's aussieht müsste da z.b. noch ein großer teil zu futures dazukommen) - oder einfach hier in einem neuen thread die jeweiligen änderungen posten, bevor ich sie in den veröffentlichten artikeln eintrage?
-
Also ich sehe da drei Möglichkeiten:
1. Du machst weiteren Teil, wenn sich das neue mit dem alten nicht wirklich überschneidet.
2. Du ergänzt den Artikel hier und er wird korrekturgelesen und dann nochmal veröffentlicht - der alte verschwindet dann. (Macht das unser Portal mit?)
3. Du wartest mit der Veröffentlichung bis alles eingearbeitet ist.Ich werde mit der heutigen Veröffentlichung möglichst lange warten, damit ich deine Entscheidung berücksichtigen kann. Dein Artikel ist ja dieses Mal der einzige.
-
queer_boy schrieb:
mann, jetzt sehe ich mir doch tatsächlich nochmal den artikel zu C++ in der wikipedia genauer an, und ich glaube es kaum: der typ, der den link rausgelöscht hat, fügt ja absichtlich falsche sachen ein und beruft sich dabei noch auf eine (veraltete) quelle...
Bin gespannt was die auf deine Fragen in der Diskussion antworten. Du bist absolut im recht und hast zwei wirklich tolle Artikel geschrieben Das werden nun wohl meine neuen Lieblingsartikel!
MfG SideWinder
-
estartu schrieb:
Also ich sehe da drei Möglichkeiten:
1. Du machst weiteren Teil, wenn sich das neue mit dem alten nicht wirklich überschneidet.
2. Du ergänzt den Artikel hier und er wird korrekturgelesen und dann nochmal veröffentlicht - der alte verschwindet dann. (Macht das unser Portal mit?)
3. Du wartest mit der Veröffentlichung bis alles eingearbeitet ist.Ich werde mit der heutigen Veröffentlichung möglichst lange warten, damit ich deine Entscheidung berücksichtigen kann. Dein Artikel ist ja dieses Mal der einzige.
hm zu 1. - ich glaube nicht, dass sich noch so viel überraschendes ergeben wird. concepts werden jetzt wohl fix aufgenommen werden; das einzige, was ich da ändern müsste, wäre der eine satz unter "status" (concepts sind jetzt teil des working drafts); sowie "der working draft ist feature complete" oä. sogar Date/Time könnte, wenn keine zeit für das komitee ist, wieder im originalwortlaut aufgenommen werden ( )
zu 3. - das ginge vielleicht noch mit diesem artikel, aber nicht mit dem der schon draußen ist. da ist es mir lieber, wenn ich dann einfach hier die größeren ergänzungen/überarbeitungen poste und der artikel dann neu veröffentlicht wird (aber wieso gleich so drastisch? wir könnten doch auch einfach diesen [X]-thread hier lassen und sobald ich was überarbeitet habe, poste ich; wenn dann korrekturgelesen ist, wird einfach hinübergepastet - oder ist das auch nicht gut für das portal?)
vor allem denke ich, dass es gut ist, den artikel einmal zu veröffentlichen. bis die post-meeting mails rauskommen dürfte es ja wohl auch noch einige zeit lang dauern.
-
Ich denke, es ist auch kein Problem, wenn du einfach die entsprechenden Abschnitte im Original-Artikel, d.h. im Subforum „Die Artikel“, editierst (wenn das die Forumsberechtigungen zulassen) und mir hier dann mitteilst, welche Abschnitte das sind.
Bei deiner guten Rechtschreibung (das wenige, was ich korrigierte, waren fast nur Tippfehler) ist das denke ich vertretbar.
Edit: den Artikel würde ich auf jeden Fall heute veröffentlichen.