C++20: Compiler-Funktionen mit Feature-Testing-Makros ermitteln
Ein relativ unbekanntes Feature in C + + 20 sind die Testmakros für Features. Sie geben die definitive Antwort, welches C++-Feature der Compiler unterstützt.​

(Bild: iX mit KI (Dall-E 3))
- Rainer Grimm
C++20 bringt die Featuretestmakros mit, die den verwendeten Compiler daraufhin untersuchen, welche Features er unterstĂĽtzt.
Wer die neuesten C++-Features ausprobiert, erhält oft Fehlermeldungen. Jetzt stellt sich die Frage: Wer ist dafür verantwortlich?
- Hat man selbst einen Fehler gemacht?
- UnterstĂĽtzt der verwendete Compiler dieses Feature?
- Ist ein Compiler-Bug aufgetreten?
Option 3 tritt eher selten auf, also muss man nur eine Frage beantworten: UnterstĂĽtzt der Compiler dieses Feature?
Diese Frage ist dank der cppreference und der Feature-Testmakros leicht zu beantworten.
C++ Compiler Support
Wie so oft gibt cppreference.com/compiler_support Antworten auf Fragen zur Kernsprache und Bibliothek, einschlieĂźlich der C++11-Standards bis hin zu C++26.
Die folgenden Tabellen geben einen Ăśberblick ĂĽber die UnterstĂĽtzung der Kernsprache und der Bibliothek fĂĽr den C++26-Standard.
C++26 Kernsprache
C++26 Bibliothek
Wenn die Antworten nicht spezifisch genug sind, helfen die Featuretestmakros in C++20.
Feature-Testing-Makros
Mit dem Header <version>
kann man den Compiler nach seiner Unterstützung für C++11 oder höher fragen. Man kann nach Attributen, Features der Kernsprache oder der Bibliothek fragen. <version>
hat mehr als 300 Makros definiert, die sich zu einer Zahl erweitern, wenn das Feature implementiert ist. Die Zahl steht fĂĽr das Jahr und den Monat, in dem das Feature zum C++-Standardentwurf hinzugefĂĽgt wurde. Dies sind die Nummern fĂĽr static_assert, Lambdas
und Concepts
.
__cpp_static_assert 200410L
__cpp_lambdas 200907L
__cpp_concepts 201907L
Die Seite Feature Testing Macros fĂĽhrt alle Makros auf.
Das folgende Programm, das ich von cppreference ĂĽbernommen und angepasst habe, zeigt alle Makros der Kernsprache C++20 an. Das Flag /Zc:__cplusplus
aktiviert das Featuretestmakro unter Windows.
// featureTesting20.cpp
// from cppreference.com
#if __cplusplus < 201100
# error "C++11 or better is required"
#endif
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <string>
#ifdef __has_include
# if __has_include(<version>)
# include <version>
# endif
#endif
#define COMPILER_FEATURE_VALUE(value) #value
#define COMPILER_FEATURE_ENTRY(name) { #name, COMPILER_FEATURE_VALUE(name) },
#ifdef __has_cpp_attribute
# define COMPILER_ATTRIBUTE_VALUE_AS_STRING(s) #s
# define COMPILER_ATTRIBUTE_AS_NUMBER(x) COMPILER_ATTRIBUTE_VALUE_AS_STRING(x)
# define COMPILER_ATTRIBUTE_ENTRY(attr) \
{ #attr, COMPILER_ATTRIBUTE_AS_NUMBER(__has_cpp_attribute(attr)) },
#else
# define COMPILER_ATTRIBUTE_ENTRY(attr) { #attr, "_" },
#endif
// Change these options to print out only necessary info.
static struct PrintOptions {
constexpr static bool titles = 1;
constexpr static bool attributes = 1;
constexpr static bool general_features = 1;
constexpr static bool core_features = 1;
constexpr static bool lib_features = 1;
constexpr static bool supported_features = 1;
constexpr static bool unsupported_features = 1;
constexpr static bool sorted_by_value = 0;
constexpr static bool cxx11 = 1;
constexpr static bool cxx14 = 1;
constexpr static bool cxx17 = 1;
constexpr static bool cxx20 = 1;
constexpr static bool cxx23 = 0;
} print;
struct CompilerFeature {
CompilerFeature(const char* name = nullptr, const char* value = nullptr)
: name(name), value(value) {}
const char* name; const char* value;
};
static CompilerFeature cxx20[] = {
COMPILER_FEATURE_ENTRY(__cpp_aggregate_paren_init)
COMPILER_FEATURE_ENTRY(__cpp_char8_t)
COMPILER_FEATURE_ENTRY(__cpp_concepts)
COMPILER_FEATURE_ENTRY(__cpp_conditional_explicit)
COMPILER_FEATURE_ENTRY(__cpp_consteval)
COMPILER_FEATURE_ENTRY(__cpp_constexpr)
COMPILER_FEATURE_ENTRY(__cpp_constexpr_dynamic_alloc)
COMPILER_FEATURE_ENTRY(__cpp_constexpr_in_decltype)
COMPILER_FEATURE_ENTRY(__cpp_constinit)
COMPILER_FEATURE_ENTRY(__cpp_deduction_guides)
COMPILER_FEATURE_ENTRY(__cpp_designated_initializers)
COMPILER_FEATURE_ENTRY(__cpp_generic_lambdas)
COMPILER_FEATURE_ENTRY(__cpp_impl_coroutine)
COMPILER_FEATURE_ENTRY(__cpp_impl_destroying_delete)
COMPILER_FEATURE_ENTRY(__cpp_impl_three_way_comparison)
COMPILER_FEATURE_ENTRY(__cpp_init_captures)
COMPILER_FEATURE_ENTRY(__cpp_modules)
COMPILER_FEATURE_ENTRY(__cpp_nontype_template_args)
COMPILER_FEATURE_ENTRY(__cpp_using_enum)
};
constexpr bool is_feature_supported(const CompilerFeature& x) {
return x.value[0] != '_' && x.value[0] != '0' ;
}
inline void print_compiler_feature(const CompilerFeature& x) {
constexpr static int max_name_length = 44; //< Update if necessary
std::string value{ is_feature_supported(x) ? x.value : "------" };
if (value.back() == 'L') value.pop_back(); //~ 201603L -> 201603
// value.insert(4, 1, '-'); //~ 201603 -> 2016-03
if ( (print.supported_features && is_feature_supported(x))
|| (print.unsupported_features && !is_feature_supported(x))) {
std::cout << std::left << std::setw(max_name_length)
<< x.name << " " << value << '\n';
}
}
template<size_t N>
inline void show(char const* title, CompilerFeature (&features)[N]) {
if (print.titles) {
std::cout << '\n' << std::left << title << '\n';
}
if (print.sorted_by_value) {
std::sort(std::begin(features), std::end(features),
[](CompilerFeature const& lhs, CompilerFeature const& rhs) {
return std::strcmp(lhs.value, rhs.value) < 0;
});
}
for (const CompilerFeature& x : features) {
print_compiler_feature(x);
}
}
int main() {
if (print.cxx20 && print.core_features) show("C++20 CORE", cxx20);
}
Die folgenden Screenshots aus dem Compiler Explorer zeigen die C++20 UnterstĂĽtzung der Compiler GCC 8.1, 10.1 und 14.1.
- GCC 8.1
- GCC 10.1
- GCC 14.1
Wie geht's weiter?
In meinem nächsten Artikel setze ich meine Reise durch C++ 23 fort. (rme)