Blog 5 Min. Lesezeit

Das Beste von C++ kommt erst noch

Diesen Artikel teilen
The best of C++ is what's coming

Seit 2011 wurden dem C++-Standard zahlreiche neue Funktionen hinzugefügt. Einige, wie auto und nullptr, sind inzwischen weit verbreitet, während andere noch vergleichsweise selten eingesetzt werden. Einige der bedeutendsten Neuerungen – jene mit dem Potenzial, die Sprache auf die nächste Stufe zu heben – befinden sich jedoch noch in der Entwicklung.

Module

Der herkömmliche  

#include

 Mechanismus ist nach wie vor vorhanden und bringt zahlreiche Nachteile mit sich. Einige davon werden in diesem interessanten Dokument.

  • Skalierbarkeit der Kompilierzeit: Jedes Mal, wenn ein Header eingebunden wird, muss der Compiler den Text dieses Headers sowie transitiv aller darin eingebundenen Header vorverarbeiten und parsen. Dieser Vorgang muss für jede Übersetzungseinheit der Anwendung erneut durchgeführt werden, was zu enorm viel redundanter Arbeit führt. In einem Projekt mit Ntranslation units and M headers included in each translation unit, the compiler is performing M x N work even though most of the M Header werden von mehreren Übersetzungseinheiten gemeinsam verwendet. Bei C++ ist das besonders problematisch, weil das Kompilierungsmodell für Templates dazu zwingt, sehr viel Code in Headern unterzubringen.
  • Fragilität#include Direktiven werden vom Präprozessor als textuelle Einbindung behandelt und unterliegen daher allen Makrodefinitionen, die zum Zeitpunkt der Einbindung aktiv sind. Kollidiert eine dieser Makrodefinitionen mit einem Namen in der Bibliothek, kann dies die Bibliotheks-API beschädigen oder bereits im Bibliotheks-Header zu Kompilierungsfehlern führen. Ein extremes Beispiel: #define std "The C++ Standard" und anschließend einen Header der Standardbibliothek einbindet: Das Ergebnis ist eine erschreckende Kaskade von Fehlern in der Implementierung der C++-Standardbibliothek. Subtilere Probleme treten in der Praxis auf, wenn Header zweier verschiedener Bibliotheken aufgrund von Makrokollisionen miteinander interagieren und Benutzer gezwungen sind, die Reihenfolge #include directives or introduce #undef directives to break the (unintended) dependency.
  • Herkömmliche Workarounds: C-Programmierer haben eine Reihe von Konventionen entwickelt, um die Fragilität des C-Präprozessormodells zu umgehen. Include Guards sind beispielsweise für die große Mehrheit der Header erforderlich, damit mehrfaches Einbinden die Kompilierung nicht beeinträchtigt. Makronamen werden mitLONG_PREFIXED_UPPERCASE_IDENTIFIERS to avoid collisions, and some library/framework developers even use __underscored Namen in Headern geschrieben, um Kollisionen mit „normalen“ Namen zu vermeiden, die gemäß Konvention überhaupt keine Makros sein sollten. Diese Konventionen erschweren Entwicklern aus Nicht-C-Sprachen den Einstieg, sind für erfahrenere Entwickler unnötiger Boilerplate und machen unsere Header deutlich unübersichtlicher, als sie sein müssten.
  • Probleme für Entwicklungswerkzeuge: In einer C-basierten Sprache ist es schwierig, Werkzeuge zu entwickeln, die zuverlässig mit Softwarebibliotheken arbeiten, weil deren Grenzen nicht eindeutig sind. Welche Header gehören zu einer bestimmten Bibliothek und in welcher Reihenfolge müssen sie eingebunden werden, damit sie korrekt kompilieren? Handelt es sich um C-, C++-, Objective-C++-Header oder um eine Variante dieser Sprachen? Welche Deklarationen in diesen Headern sind tatsächlich Teil der API und welche sind nur vorhanden, weil sie im Header stehen mussten?

Module verbessern den Zugriff auf Bibliotheken durch ein robusteres und effizienteres semantisches Modell. Aus Sicht des Anwenders unterscheidet sich der Code nur geringfügig, da eine

import

 Deklaration anstelle einer 

#include

Präprozessor-Direktive verwendet wird:

import std; // Module import directive.
int main() {
    std::cout << “Hello World\n”;
}

Es ist nicht mehr nötig, zahlreiche STL-Header einzubinden – ein einziger Import genügt, wodurch der Code übersichtlicher wird. Der Modulimport lädt eine binäre Repräsentation des

std 

Moduls und stellt dessen API der Anwendung direkt zur Verfügung. Präprozessor-Definitionen vor der Importdeklaration haben keinen Einfluss auf die von

std

bereitgestellte API, da das Modul selbst separat und eigenständig kompiliert wurde.

Metaklassen

Herb Sutter trägt seit vielen Jahren zur Weiterentwicklung von C++ bei und schlug im vergangenen Jahr das Metaklassen-Feature vor. In seinem Beitrag:

Ich arbeite an einem experimentellen neuen C++-Sprachfeature mit dem vorläufigen Namen „Metaklassen“, das die C++-Programmierung zugleich leistungsfähiger und einfacher machen soll.

Und hier folgt eine Definition von Metaklassen sowie der Vorteile ihrer Verwendung aus dem Vorschlag:

Metaklassen (vorläufige Bezeichnung) ermöglichen Programmierern eine neue Form effizienter Abstraktion: eine benutzerdefinierte, benannte Teilmenge von Klassen mit gemeinsamen Eigenschaften – einschließlich benutzerdefinierter Regeln, Standardwerte und generierter Funktionen. Dazu wird eine individuelle Transformation von normalem C++-Quellcode in eine normale C++-Klassendefinition beschrieben. Es entsteht keine Aufspaltung des Typsystems; die erzeugte Klasse ist eine ganz normale Klasse.Hauptziele: • Das Abstraktionsvokabular von C++ über class/struct/union/enum hinaus erweitern, also über jene Typkategorien, die fest in der Sprache verankert sind.• Bewährte Best Practices als wiederverwendbare Bibliotheken statt als englischsprachige Leitfäden oder Bücher bereitstellen. So entsteht ein leicht übernehmbares Vokabular (z. B. interface, value), anstatt Listen von Regeln auswendig lernen zu müssen.• Vom Compiler erzwungene Muster für beliebige Zwecke definieren können: Coding-Standards, API-Anforderungen und andere Regeln für Klassen.• Viele neue Funktionen für „spezialisierte Typen“ als gewöhnlichen Bibliothekscode statt als schwer lesbaren Standardtext formulieren. Dadurch können sie mit normalen Werkzeugen getestet und debuggt, unabhängig von neuen Compiler-Versionen entwickelt und verteilt sowie als Code standardisiert werden.standardese, bei gleicher Benutzerfreundlichkeit und Effizienz, sodass sie mit normalen Werkzeugen per Unit-Test geprüft und debuggt, ohne Aktualisierung oder Auslieferung eines neuen Compilers entwickelt und verteilt sowie als Code über LEWG/LWG statt als standardese. Dadurch lassen sich wertvolle Erweiterungen standardisieren, die wir aufgrund ihres engen Anwendungsbereichs vermutlich nie in die Kernsprache aufnehmen würden (z. B. interface), die sich aber problemlos als kleine, eigenständige Bibliothek standardisieren lassen. • Die Notwendigkeit beseitigen, spezielle Nicht-C++-„Nebensprachen“ und eigene Compiler wie Qt moc, COM MIDL oder C++/CX zu erfinden, nur um Informationen auszudrücken, die sich im heutigen C++ nicht darstellen lassen.

Mit Modulen und Metaklassen könnte C++ zugleich leistungsfähiger und einfacher zu verwenden werden. In Zukunft dürften noch viele weitere Funktionen hinzukommen. Uns bleibt nur zu sagen: Vielen Dank an alle Mitwirkenden, die C++ kontinuierlich weiterentwickeln – und lang lebe C++! :)

Diesen Artikel teilen