Bonjour à tous ! Aujourd’hui je vous propose de nous intéresser aux nouvelles fonctions du préprocesseur pour le feature testing, qui ont été acceptées pour C++20.
La P0941 introduit certaines nouveautés dans le but de permettre de tester la présence d’une fonctionnalité du langage ou d’un élément de la bibliothèque standard sur un compilateur. Ceci vient compléter __has_include que nous avait fourni C++17.
Chaque macro de feature-testing est donc définie si le compilateur / la plateforme supporte la fonctionnalité correspondante. Dans ce cas, la valeur est un entier (pp-number) basé sur la date d’ajout de la fonctionnalité au draft du standard :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#if !__cpp_lambdas # include "MyFunctor.hpp" #endif int main() { #if __cpp_lambdas auto func = [&baz](Foo& foo) { return bar(foo); } #else MyFunctor func(baz); #endif func(argv[0]); return 0; } |
Lorsqu’une fonctionnalité donnée subit un changement significatif, la valeur est mise-à-jour ce qui permet de tester la présence d’une version particulière d’une feature :
1 2 3 4 5 6 7 |
#if __cpp_lib_chrono >= <span class="t-c"><span class="mw-geshi cpp source-cpp"><span class="nu0">201611L</span></span></span> // <chrono> constexpr est supporté #elif __cpp_lib_chrono >= 201510L // <chrono> est supporté #else // <chrono> n'est pas supporté #endif |
Cette page de cppreference.com propose une liste des différentes macros de feature-testing disponibles avec C++20. La majorité des fonctionnalités introduites depuis C++11 sont associées à une macro, en voici quelques exemples :
- C++11 : __cpp_decltype, __cpp_unicode_literals azdazd …
- C++14 : __cpp_variable_templates, __cpp_sized_deallocation …
- C++17 : __cpp_inline_variables, __cpp_enumerator_attributes …
- C++20 : __cpp_impl_three_way_comparison, cpp_nontype_template_parameter_class …
De plus, C++20 voit l’ajout de la directive __has_cpp_attribute qui permet de tester la présence d’un attribut. De la même manière, la valeur renvoyée est un entier correspondant à l’année et au mois de la publication dans le working draft. Dans le cas d’un attribut non-standard, la valeur est non-nulle si l’attribut est présent, laissée au choix de l’implémentation.
Il est donc possible de tester le support d’un attribut à la compilation :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#if __has_cpp_attribute(nodiscard) # define NODISCARD [[nodiscard]] #elif __has_cpp_attribute(gcc::nodiscard) # define NODISCARD [[gcc::nodiscard]] #else # define NODISCARD __attribute__((warn_unused_result)) #endif #if !__has_cpp_attribute(omp::parallel) # error OpenMP not available #endif #if __has_cpp_attribute(unlikely) # define UNLIKELY [[unelikely]] #else # define UNLIKELY #endif NODISCARD bool foo(int i) { if(UNLIKELY i > 0) return true; return false; } |
Notez par ailleurs que __has_cpp_attribute n’est utilisable que dans les directives préprocesseurs conditionnelles #if et #elif (et #ifdef, #ifndef, defined). Le code suivant n’est donc pas valide (même si clang et gcc semblent l’accepter tout de même) :
1 |
std::cout << __has_cpp_attribute(nodiscard) << std::endl; |
Bien entendu, cela implique qu’un compilateur ne supportant pas C++20 ne supportera pas ces macros. L’opérateur préprocesseur defined peut être utilisé pour tester en amont la présence de la macro :
1 2 3 4 5 |
#if defined(__has_cpp_attribute) && __has_cpp_attribute(fallthrough) # define FALLTHROUGH [[fallthrough]] #else # define FALLTHROUGH #endif |
C’est tout pour aujourd’hui ! Je vous dis à bientôt pour un nouvel article.
You must log in to post a comment.