Im Grunde gibt es drei Wege den C++-Standard in CMake für das gesamte Projekt respektive für einzelne Targets zu aktivieren. CMake übergibt bei jeder der hier aufgezeigten Möglichkeiten die korrekten Compiler- und Linker-Flags, sodass der angegebene C++-Standard im Code verwendet werden kann. Auf die verschiedenen Wege respektive Optionen werde ich im Folgenden einmal eingehen. Den Code, den ich den Beispielen dieses Artikels vorstelle, könnt ihr gerne weiter verwenden, ihr findet ihn dazu auf GitHub.
Diesen und andere Beiträge von mir kannst du als sauber formatierte PDF-Datei zum Ausdrucken oder offline Lesen erwerben. Mehr Informationen dazu findest du hier.
Nötige Vorkenntnisse
- CMake Grundkenntnisse: Dazu gehört etwa die Erstellung und Kompilierung eines ausführbaren Programms mit CMake. Ihr solltet dahin gehend auch mit den CMake-Befehlen cmake_minimum_required(), project() und add_executable() vertraut sein. Solltet ihr nicht wissen, wie ihr ein Programm mit CMake erstellt und kompiliert, schaut euch am besten zuvor diesen Artikel oder dieses YouTube-Video von mir an.
- CMake-Variablen: CMake-Variablen stelle ich sowohl in einem Artikel als auch in einem YouTube-Video vor. Zudem findet sich eine Erklärung zu CMake-Variablen auch in der CMake-Dokumentation.
- CMake-Properties: In diesem Video auf YouTube gehe ich auf Properties in CMake ein. Zudem findet man eine Auflistung von Properties in der CMake-Dokumentation, eine genauere Erläuterung, was Properties eigentlich sind, fehlt hier jedoch.
- Unterordner einbinden: In CMake können Unterordner, die eine CMakeLists.txt-Datei enthalten, mittels des add_subdirectory()-Befehls eingebunden werden. Vereinfacht gesagt kann man sich vorstellen, dass die eingebundene CMakeLists.txt-Datei an die Stelle des add_subdirectory()-Befehls kopiert wird. Falls ihr weitere Informationen benötigt, findet ihr diese auch in der CMake-Dokumentation.
- Ich verwende die Befehle target_link_libraries() und target_include_directories(), um Bibliotheken zu verlinken und Include-Ordner zu setzen. Mehr Informationen findet ihr in der CMake-Dokumentation (Link1, Link2) oder in diesen beiden YouTube Videos (Link1, Link2).
- Alle hier benötigten Vorkenntnisse findet ihr auch in meinem Buch CMake für Einsteiger.
Option 1: Verwendung von CMake-Variablen
Der vermutlich einfachste Weg, den C++-Standard für das eigene Projekt zu aktivieren, ist das Setzen der CMake-Variablen CMAKE_CXX_STANDARD auf den benötigten C++-Standard. Es empfiehlt sich ebenfalls die CMake-Variable CMAKE_CXX_STANDARD_REQUIRED auf TRUE respektive ON zu setzen. In der CMakeLists.txt-Datei sieht das wie folgt aus:
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Valide Werte für die Variable CMAKE_CXX_STANDARD sind 98, 11, 14, 17 (ab CMake 3.8), 20 (ab CMake 3.12) und 23 (ab CMake 3.20). Die jeweilige Zahl steht ganz intuitiv für den entsprechenden C++-Standard, also die 98 für C++98, die 11 für C++11 usw. Jedes Target, das nach diesen beiden Zeilen von CMake erzeugt wird, wird nun automatisch mit dem C++11-Standard kompiliert.
Unterstützt der verwendete Compiler nicht den angegebenen C++-Standard, so geht CMake in der Regel automatisch auf den zuletzt unterstützten Standard zurück, ohne dies dem Anwender mitzuteilen. Durch das Setzen der Variable CMAKE_CXX_STANDARD_REQUIRED auf wahr, gibt CMake jedoch in diesem Fall eine Fehlermeldung aus. Es ist durch das Setzen dieser Variable auf ON also garantiert, dass alle folgenden Targets mit dem angegeben C++-Standard gebaut werden.
Die korrekten Flags übergeben
Neben der klassischen C++-Standard Compiler-Flags (z. B. -std=c++14 oder /std:c++14) unterstützen manche Compiler einen erweiterten C++-Standard, der zusätzliche „GNU extensions“ unterstützt. Die Compiler-Flag lautet dann zum Beispiel -std=gnu++14. Standardmäßig übergibt CMake den erweiterten Standard mit der GNU-Compiler-Flag, falls dieser vom Compiler unterstützt wird. Generell ist es jedoch geschickter, den klassischen C++-Standard zu verwenden.
Der klassischen C++-Standard ist der offizielle, von der Standard C++ Foundation, festgelegte C++-Standard und damit für alle verbindlich. Damit ist sichergestellt, dass dieser Standard vollständig vom Compiler unterstützt wird. Somit wird Code, der den klassischen C++-Standard verwendet, von allen Compilern verstanden. Wird der erweiterte C++-Standard verwendet, ist nicht garantiert, dass jeder Compiler alle Erweiterungen unterstützt und versteht. Somit schränkt man sich automatisch in der Wahl seiner Compiler ein.
Um CMake verständlich zu machen, dass „nur“ der klassische C++-Standard verwendet werden soll, muss die CMake Variable CMAKE_CXX_EXTENSIONS auf FALSE respektive OFF gesetzt werden.
set(CMAKE_CXX_EXTENSIONS OFF)
Beispiel 1: Setzen des C++-Standards mittels CMake-Variablen
Das erste Beispiel ist sehr einfach gehalten. Es soll lediglich eine ausführbare Datei cpp17test erzeugt werden, die zudem mit dem C++17 Standard kompiliert wird. Die entsprechende CMakeLists.txt-Datei kann dazu wie folgt aussehen:
cmake_minimum_required(VERSION 3.7)
project(
cpp_standard_project
LANGUAGES CXX
)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(cpp17test main.cpp)
In den Zeilen 8-10 werden die oben beschriebenen CMake-Variablen verwendet, um den C++17-Standard für alle folgenden Targets zu aktivieren. Oder besser gesagt: CMake übergibt durch das Setzen dieser Variablen dem Compiler die korrekten Flags, sodass dieser bei der Kompilierung den C++17-Standard verwendet. Das gilt somit auch für die darauffolgende erstellte, ausführbare Datei cpp17test in Zeile 12. Die dort verwendete Datei main.cpp sieht wie folgt aus:
#include <iostream>
int main()
{
int position[2] = {1, 2};
auto [x, y] = position;
std::cout << "Position: " << x << " " << y << std::endl;
return 0;
}
In Zeile 6 wird ein „structured binding“ aus dem C++17-Standard verwendet, um die Werte aus dem Array position in die beiden Variablen x und y zu schreiben. Diese beiden Variablen werden in Zeile 8 ausgegeben. So kann getestet werden, ob das Programm auch mit dem C++17 Standard kompiliert wird. Beispielhaft sieht dann die Ausgabe der erzeugten ausführbaren Datei cpp17test unter Ubuntu 20.04 wie folgt aus:
$ ./cpp17test
Position: 1 2
Beispiel 2: Verschachtelte CMake-Projekte
In CMake ist es möglich, ein Projekt über mehrere Ordner zu verschachteln und weitere CMakeLists.txt-Dateien in diesen Unterordnern einzubinden. In diesem Beispiel nutze ich dazu den Befehl add_subdirectory(). Durch diesen Befehl wird der dort angegebene Unterordner in das Projekt eingebunden, wobei sich in diesem Ordner ebenfalls eine CMakeLists.txt-Datei befinden muss. Sehr vereinfacht kann man sich vorstellen, dass der Inhalt der CMakeLists.txt-Datei in die übergeordnete CMakeLists.txt-Datei reinkopiert wird.
Blicken wir aber nun einmal auf ein solch verschachteltes CMake-Projekt, bei der sich in einem Unterordner libs/math die Bibliothek „mathe“ nebst einer zusätzlichen CMakeLists.txt-Datei befindet. Damit ihr einen groben Überblick über die Struktur dieses CMake-Projektes bekommt, habe ich sie hier einmal dargestellt:
Beispiel_2
│
└───libs
│ └───math
│ │ CMakeLists.txt
│ │ mathe.cpp
│ │ mathe.h
│
└───src
│ │ main.cpp
│
│ CMakeLists.txt
Blicken wir zunächst auf die übergeordnete beziehungsweise Haupt-CMakeLists.txt-Datei im Ordner Beispiel_2 (Zeile 12).
cmake_minimum_required(VERSION 3.7)
project(main_project LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
message("main_project Standard before add_sub: ${CMAKE_CXX_STANDARD}")
add_subdirectory(libs/math)
message("main_project Standard after add_sub: ${CMAKE_CXX_STANDARD}")
add_executable(hello_math src/main.cpp)
target_link_libraries(hello_math PRIVATE mathe)
In Zeile 5-7 werden erneut die benötigten CMake-Variablen gesetzt. Der Wert der CMake-Variablen CMAKE_CXX_STANDARD wird in Zeile 9 und 13 ausgegeben, also einmal vor und einmal nach dem add_subdirectory()-Befehl. Die dort verwendete CMakeLists.txt-Datei schauen wir uns im Anschluss genauer an. In Zeile 16 wird das erstellte Target hello_math mit der Bibliothek mathe verlinkt. Die Bibliothek mathe wird in der CMakeLists.txt-Datei im Unterordner libs/math erstellt:
cmake_minimum_required(VERSION 3.7)
project(sub_project LANGUAGES CXX)
message("sub_project Standard before set: ${CMAKE_CXX_STANDARD}")
set(CMAKE_CXX_STANDARD 17)
message("sub_project Standard after set: ${CMAKE_CXX_STANDARD}")
add_library(mathe STATIC mathe.cpp)
target_include_directories(mathe PUBLIC .)
In Zeile 5 wird erneut der Wert der Variablen CMAKE_CXX_STANDARD ausgegeben. Anschließend wird dieser Variablen ein anderer Wert (17) in Zeile 7 zugewiesen und in Zeile 9 der Wert der Variablen CMAKE_CXX_STANDARD erneut ausgegeben. In Zeile 11 und 12 wird dann die in der Haupt-CMakeLists.txt-Datei benötigte Bibliothek mathe erstellt und der benötigte include Ordner durch den Befehl target_include_directories() gesetzt.
Schauen wir uns nun die Ausgabe beim Ausführen von CMake an, wobei ich den hier uninteressanten Teil ausspare:
$ cmake ..
...
main_project Standard before add_sub: 11
sub_project Standard before set: 11
sub_project Standard after set: 17
main_project Standard after add_sub: 11
...
Wenig überraschend ist der gespeicherte Wert in der Variablen CMAKE_CXX_STANDARD in der ersten Ausgabe der ersten CMakeLists.txt-Datei gleich 11, siehe Zeile 3. In Zeile 4 befindet sich die erste Ausgabe der zweiten CMakeLists.txt-Datei. Auch dort ist der Wert der Variablen gleich 11, das heißt, der Wert wurde mit in die eingebundene CMakeLists.txt-Datei übernommen. Durch das anschließende Ändern des Wertes der Variablen verändert sich entsprechend auch die Ausgabe in Zeile 5.
Wichtig ist an dieser Stelle vorwiegend die Zeile 6. Dort sehen wir, dass der geänderte Wert der Variablen CMAKE_CXX_STANDARD nicht mit in die erste CMakeLists.txt-Datei übernommen wurde. Die Variable hat an dieser Stelle weiterhin den Wert 11. Das bedeutet, in diesem Beispiel wird das Target hello_math aus der ersten CMakeLists.txt-Datei mit dem C++11-Standard kompiliert, während das Target mathe aus der zweiten CMakeLists.txt-Datei mit dem C++17-Standard kompiliert wird.
Option 2: Verwendung von Target-Properties
In der Regel möchte man in einem Projekt den gleichen C++-Standard für alle Targets verwenden. Jedoch können für jedes Target die Properties für den C++-Standard einzeln angepasst werden, sodass theoretisch jedes Target mit einem anderen C++-Standard kompiliert werden kann. Wie das funktioniert schauen wir uns im Folgenden an, zuerst gehe ich jedoch genauer darauf ein, was Properties eigentlich sind und wie diese mit CMake-Variablen in Zusammenhang stehen.
Der Zusammenhang zwischen CMake-Variablen und Properties
Properties, oder übersetzt „Eigenschaften“, sind Variablen im Grunde sehr ähnlich. Sie besitzen einen Wert, welcher während der Laufzeit geändert werden kann. Im Gegensatz zu Variablen sind Properties jedoch an ein „CMake-Objekt“ gebunden. Es gibt verschiedene CMake-Objekte, doch an dieser Stelle ist vorwiegend das CMake-Objekt Target interessant. Targets besitzen eine Fülle von Properties, die die Kompilierung des Targets beeinflussen.
Häufig sind die Standardwerte einer Property, die Werte aus der zugehörigen CMake Variablen. CMake verwendet dazu meist die folgende Namensgebung: Die Variable hat den Namen CMAKE_ plus den Namen der Property. Hat eine Target Property etwa den Namen CXX_STANDARD, so wird der Standardwert dieser Property bestimmt durch den Wert in der Variablen CMAKE_CXX_STANDARD. Diesen Umstand hatten wir uns im ersten Beispiel dieses Artikels zunutze gemacht. Durch das Setzen der Variablen CMAKE_CXX_STANDARD (und der Variablen CMAKE_CXX_STANDARD_REQUIRED und CMAKE_CXX_EXTENSIONS) haben wir die Properties der nachfolgend erstellten Targets automatisch mit den Werten aus diesen Variablen befüllt.
Target-Properties für den C++-Standard
In CMake ist es möglich, die Werte der Properties eines jeden einzelnen Targets zu verändern. So ist es auch möglich, den C++-Standard für jedes Target einzeln zu setzen respektive zu aktivieren. In CMake ist die Manipulation von Target-Properties mit dem Befehl set_target_properties() möglich. Zum Setzen des C++-Standards kann dieser Befehl wie folgt aussehen.
set_target_properties(<TargetName>
PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
<TargetName> steht an dieser Stelle als Platzhalter für den Namen des Targets, dessen Properties man ändern möchte. Es ist auch möglich, an dieser Stelle mehrere Targets anzugeben. Auf das anschließende Keyword PROPERTIES folgen die einzelnen Properties, sowie der Wert, den man diesen Properties zuweisen möchte.
Die Property CXX_STANDARD legt den gewählten Compiler-Standard fest und kann genau die gleichen Werte annehmen wie die zugehörige Variable CMAKE_CXX_STANDARD. Selbiges gilt für die Property CXX_STANDARD_REQUIRED und die zugehörige Variable CMAKE_CXX_STANDARD_REQUIRED, die CMake einen Fehler ausgeben lassen, falls der Compiler den geforderten C++-Standard nicht unterstützt. Beachtet, dass es auch hier wichtig ist, die Property CXX_EXTENSIONS zu setzen, falls ihr dies nicht bereits global über das Setzen der Variable CMAKE_CXX_EXTENSIONS erledigt habt. Ansonsten wird, je nach Compiler, der erweiterte C++-Standard verwendet.
Beispiel 3: Unterschiedlicher C++-Standard für verschiedene Targets
Blicken wir einmal auf eine abgeänderte Version von Beispiel 1, in der zwei ausführbare Dateien erzeugt werden:
cmake_minimum_required(VERSION 3.7)
project(
cpp_standard_project
LANGUAGES CXX
)
add_executable(hello_world main.cpp)
add_executable(cpp17test main17.cpp)
set_target_properties(cpp17test
PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
Die ausführbare Datei hello_world aus Zeile 8 wird ohne weitere Angaben eines C++-Standards aus der Source-Datei main.cpp erstellt. In dieser Datei befindet sich lediglich ein einfaches „Hello World“-Programm.
Die zweiten ausführbare Datei cpp17test aus Zeile 10 wird aus der Source-Datei main17.cpp erzeugt. Diese Datei ist identisch zu der Datei main.cpp aus Beispiel 1, also wird zur Kompilierung der C++17-Standard benötigt. Diesen übergeben wir in den Zeilen 11-16 durch den oben gezeigten set_target_properties()-Befehl, in dem wir die Properties dieses Targets setzen.
Ich verzichte hier auf die Ausgabe des Build-Prozesses, da dort nichts ausgegeben wird, was für dieses Beispiel relevant ist.
Option 3: Verwendung von Target-Compiler-Features
Eine feingranulare Möglichkeit den C++-Standard in CMake zu setzen ist das direkte Setzen von Compiler-Features für ein Target. Ein Compiler-Feature ist eine bestimmte Funktionalität eines C++-Standards, zum Beispiel „const expressions“ aus dem C++11-Standard. Auf Basis der angegebenen Compiler-Features ermittelt CMake den benötigten C++-Standard für das Target. Die gängigsten Compiler werden ab CMake 3.6 unterstützt, daher sollte man bei Verwendung der Compiler-Features auf Minimum diese Version zurückgreifen. Besser ist sogar noch mindestens CMake 3.8 zu verwenden, da ab dann sogenannte „Meta-Features“ eingeführt wurden. Weiter unten mehr dazu.
Compiler-Features hinzufügen
Mit dem Befehl target_compile_features() können Compiler-Features zu einem Target hinzugefügt werden:
target_compile_features(<TargetName>
PUBLIC
cxx_variadic_templates
INTERFACE
cxx_nullptr
PRIVATE
cxx_lambdas
)
Mit diesem Befehl werden die drei Compiler-Features cxx_variadic_templates, cxx_nullptr und cxx_lambdas zum Target <TargetName> hinzugefügt, jedoch unterscheidet sich deren Anwendung durch die Verwendung der Keywords PUBLIC, INTERFACE und PRIVATE etwas. Durch Verwendung des Keywords PRIVATE wird das Compiler-Feature cxx_lambdas lediglich direkt für das Target verwendet. Im Gegensatz dazu wird das Compiler-Feature cxx_nullptr, das nach dem INTERFACE Keyword angegeben wurde, nur für Targets, die mit diesem Target verlinkt werden, aktiviert. Das Keyword PUBLIC kombiniert das Verhalten des INTERFACE und PRIVATE Keywords und aktiviert das Compiler-Feature cxx_variadic_templates sowohl für das Target <TargetName> selbst, als auch für alle verlinkten Targets. Ich erkläre die Verwendung dieser Keywords noch einmal deutlich umfangreicher in meinem YouTube-Video zum Thema Verlinken von Targets.
Eine Liste aller von CMake unterstützten Compiler-Features findet sich in der CMake-Dokumentation zur globalen Property CMAKE_CXX_KNOWN_FEATURES, die ihr über den hinterlegten Link erreichen könnt. Außerdem sind in der CMake-Variablen CMAKE_CXX_COMPILE_FEATURES alle Compiler-Features gespeichert, die der verwendete C++-Compiler unterstützt. Durch das Ausgeben dieser Variable könnt ihr euch also die von eurem Compiler unterstützten Compiler-Features ansehen.
message("Unterstützte Compiler-Features = ${CMAKE_CXX_COMPILE_FEATURES}")
Verwendung von Meta-Features
Die Verwendung der oben gezeigten Compiler-Features bringt sowohl Vor- als auch Nachteile mit sich. Zum einen kann durch Verwendung des INTERFACE und PUBLIC Keywords der benötigte C++-Standard auf weitere Targets übertragen werden. So kann sichergestellt werden, dass der benötigte C++-Standard im Interface einer Bibliothek auch auf verlinkte Targets übertragen wird. Zudem unterstützt der target_compile_features()-Befehl die Verwendung von Generator Expressions. Generator Expressions erlauben die Auswertung von Ausdrücken zur Build- respektive Installationszeit und bieten so eine größere Flexibilität. In der verlinkten CMake-Dokumentation, sowie in meinem Buch „CMake für Einsteiger“ werden Generator Expressions umfangreich erläutert. Bei Interesse an einem Blogartikel und/oder einem YouTube-Video zum Thema Generator Expressions lasst mir gerne einen Kommentar da.
Der Nachteil solcher Compiler-Features birgt sich im großen Umfang der existierenden Compiler-Features. Die Liste kann recht schnell lang und unübersichtlich werden, zudem muss diese entsprechend gepflegt und erweitert werden. Nicht nur vom Anwender selbst, sondern auch von den CMake Entwicklern. Um diesem Problem zu lösen, gibt es seit CMake 3.8 die sogenannten Meta-Features.
Meta-Features haben die Form cxx_std_<cppStd>, wobei <cppStd> für den benötigten C++-Standard steht. Hier können die gleichen Zahlen wie bei der Variablen CMAKE_CXX_STANDARD (siehe Option 1) eingesetzt werden, also zum Beispiel cxx_std_11, cxx_std_14 usw. Diese Meta-Features können dann wie jedes andere Compiler-Feature auch im target_compile_features()-Befehl verwendet werden und aktivieren den angegebenen C++-Standard für dieses Target. Überschneiden sich Anforderungen aus den unterschiedlichen genannten Optionen (CMake-Variablen, Properties und Compiler-Features) für den C++-Standard, so wählt CMake automatisch den höchsten benötigten C++-Standard aus. Zum Beispiel würde hier
set_target_properties(<TargetName> PROPERTIES CXX_STANDARD 11)
target_compile_features(<TargetName> PUBLIC cxx_std_14)
das Target <TargetName> mit dem C++14-Standard gebaut werden. Aufgrund der genannten Nachteile werden Compiler-Features nur für den C++98-, C++11- und C++14-Standard unterstützt. Ab C++17 gibt es nur noch die oben beschriebenen Meta-Features cxx_std_17, cxx_std_20 usw.
Beispiel 4: Verwendung von Target-Compiler- und Meta-Features
Betrachten wir zum Abschluss das folgende, von Beispiel 3 abgewandelte Beispiel:
cmake_minimum_required(VERSION 3.8)
project(
cpp_standard_project
LANGUAGES CXX
)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
message("Unterstützte Compiler-Features = ${CMAKE_CXX_COMPILE_FEATURES}")
add_executable(hello_world main.cpp)
add_executable(cpp17test main17.cpp)
target_compile_features(cpp17test PRIVATE cxx_std_17)
Zunächst werden in Zeile 8-10 die bereits bekannten CMake-Variablen für den C++11-Standard gesetzt. Zudem werden in Zeile 12 die durch den Compiler unterstützten Compiler-Features ausgegeben. Die ausführbare Datei hello_world in Zeile 14 wird also mit dem C++11-Standard erstellt. Die ausführbare Datei cpp17test in Zeile 16 benötigt jedoch den C++17-Standard, der C++11-Standard ist nicht ausreichend. Durch Verwendung des target_compile_features()-Befehl und des Meta-Features cxx_std_17 in Zeile 17 lösen wir dieses Problem und das Target cpp17test wird mit dem C++17-Standard kompiliert. Die Ausgabe der unterstützten Compiler-Features sieht dabei auf meinem System (Ubuntu 20.04) und dem GNU 9.4.0 Compiler wie folgt aus:
$ cmake ../
-- The CXX compiler identification is GNU 9.4.0
...
Unterstützte Compiler-Features =
cxx_std_98; cxx_template_template_parameters; cxx_std_11;
cxx_alias_templates; cxx_alignas; cxx_alignof; cxx_attributes; cxx_auto_type; cxx_constexpr;
cxx_decltype; cxx_decltype_incomplete_return_types; cxx_default_function_template_args;
cxx_defaulted_functions; cxx_defaulted_move_initializers; cxx_delegating_constructors;
cxx_deleted_functions; cxx_enum_forward_declarations; cxx_explicit_conversions;
cxx_extended_friend_declarations; cxx_extern_templates; cxx_final; cxx_func_identifier;
cxx_generalized_initializers; cxx_inheriting_constructors; cxx_inline_namespaces; cxx_lambdas;
cxx_local_type_template_args; cxx_long_long_type; cxx_noexcept; cxx_nonstatic_member_init; cxx_nullptr;
cxx_override; cxx_range_for; cxx_raw_string_literals; cxx_reference_qualified_functions;
cxx_right_angle_brackets; cxx_rvalue_references; cxx_sizeof_member; cxx_static_assert;
cxx_strong_enums; cxx_thread_local; cxx_trailing_return_types; cxx_unicode_literals;
cxx_uniform_initialization; cxx_unrestricted_unions; cxx_user_literals; cxx_variadic_macros;
cxx_variadic_templates; cxx_std_14; cxx_aggregate_default_initializers; cxx_attribute_deprecated;
cxx_binary_literals; cxx_contextual_conversions; cxx_decltype_auto; cxx_digit_separators;
cxx_generic_lambdas; cxx_lambda_init_captures; cxx_relaxed_constexpr; cxx_return_type_deduction;
cxx_variable_templates; cxx_std_17; cxx_std_20
...
In der Ausgabe habe ich zwischen den einzelnen Compiler-Features Leerzeichen und Zeilenumbrüche eingefügt, da die Darstellung ansonsten zu unübersichtlich geworden wäre.
Zusammenfassung C++-Standard in CMake
In einem CMake-Projekt ist es meist vollkommen ausreichend, den C++-Standard über die unter Option 1 genannten CMake-Variablen zu setzen. Benötigt man einen unterschiedlichen C++-Standard in seinem Projekt, so kann auf die Property oder die Compiler-Features Variante zurückgegriffen werden, wobei ich in der Regel auf Meta-Features und nicht auf einzelne Compiler-Features zurückgreifen würde. Werden Generator Expressions benötigt und/oder der verwendete C++-Standard muss an verlinkte Targets weitergegeben werden, so sind Compiler- respektive Meta-Features die logische Wahl.
Weitere Informationen
Den Code zu diesem Beitrag könnt ihr gerne weiter verwenden, ihr findet ihn dazu auf GitHub.
In meinem Buch „CMake für Einsteiger“ und in meinen Videos auf YouTube stelle ich dieses und weitere CMake Themen noch einmal detaillierter vor. Bei Fragen oder Anmerkungen schreibt mir gerne einen Kommentar 🙂
Meine Webseite ist komplett werbefrei. Falls dir dieser Beitrag gefallen hat und du meine Arbeit gerne unterstützen möchtest, schau daher doch einmal auf meiner Support-Seite vorbei. Das würde mich sehr freuen :).