web-dev-qa-db-fra.com

Détectez-vous la prise en charge de __VA_OPT__?

En C++ 20, le préprocesseur prend en charge __VA_OPT__ comme moyen de développer éventuellement des jetons dans une macro variadique si le nombre d'arguments est supérieur à zéro. (Cela évite d'avoir à utiliser le ##__VA_ARGS__ Extension GCC, qui est un hack non portable et moche.)

Clang SVN a implémenté cette fonctionnalité, mais ils n'ont pas ajouté de macro de test de fonctionnalité pour cela. Tout pirate intelligent de préprocesseur peut-il trouver un moyen de détecter la présence ou l'absence de __VA_OPT__ support sans provoquer d'erreur matérielle ou d'avertissement de portabilité?

24
Eric Niebler

Inspiré par réponse de chris .

#define PP_THIRD_ARG(a,b,c,...) c
#define VA_OPT_SUPPORTED_I(...) PP_THIRD_ARG(__VA_OPT__(,),true,false,)
#define VA_OPT_SUPPORTED VA_OPT_SUPPORTED_I(?)

Si __VA_OPT__ Est pris en charge, VA_OPT_SUPPORTED_I(?) se développe en PP_THIRD_ARG(,,true,false,), donc le troisième argument est true; sinon, VA_OPT_SUPPORTED_I(?) se développe en PP_THIRD_ARG(__VA_OPT__(,),true,false,), le troisième argument est false.

30
cpplearner

Quelque chose comme ce qui suit devrait fonctionner, bien que vous puissiez l'améliorer:

#include <boost/preprocessor.hpp>

#define VA_OPT_SUPPORTED_II_1(_) 0
#define VA_OPT_SUPPORTED_II_2(_1, _2) 1

#define VA_OPT_SUPPORTED_I(...) BOOST_PP_OVERLOAD(VA_OPT_SUPPORTED_II_, __VA_OPT__(,))(__VA_OPT__(,))

#define VA_OPT_SUPPORTED VA_OPT_SUPPORTED_I(?)

Sur le tronc Clang, cela correspond à 1 en mode C++ 2a et à 0 en mode C++ 17. Le tronc GCC évalue réellement cela à 1 en C++ 17, mais gère également __VA_OPT__ Dans ce mode.

Cela permet d'utiliser BOOST_PP_OVERLOAD Pour appeler la version _1 Ou _2 De _II En fonction du nombre d'arguments. Si __VA_OPT__(,) se développe en ,, Il y aura 2 arguments vides. Sinon, il y aura 1 argument vide. Nous appelons toujours cette macro avec une liste d'arguments, donc tout compilateur prenant en charge __VA_OPT__ Doit toujours l'étendre à ,.

Naturellement, la dépendance Boost.PP n'est pas obligatoire. Une macro simple 1 ou 2 arguments OVERLOAD devrait être assez facile à remplacer. Perdre un peu de généralité pour le rendre plus simple:

#define OVERLOAD2_I(_1, _2, NAME, ...) NAME
#define OVERLOAD2(NAME1, NAME2, ...) OVERLOAD2_I(__VA_ARGS__, NAME2, NAME1)

#define VA_OPT_SUPPORTED_I(...) OVERLOAD2(VA_OPT_SUPPORTED_II_1, VA_OPT_SUPPORTED_II_2, __VA_OPT__(,))(__VA_OPT__(,))

Il y a un avertissement de portabilité de Clang:

avertissement: les macros variadiques sont incompatibles avec C++ 98 [-Wc ++ 98-compat-pedantic]

Je ne sais pas si cette détection est même possible sans le support des macros variadiques C++ 11. Vous pouvez envisager de ne pas prendre en charge les valeurs __cplusplus Inférieures à C++ 11, mais Clang donne toujours l'avertissement même lorsqu'il est encapsulé dans une telle vérification.

6
chris

Comme mentionné dans l'autre réponse, vous pouvez écrire votre propre macro OVERLOAD. BOOST_PP_OVERLOAD se compose de deux parties, BOOST_PP_CAT et BOOST_PP_VARIADIC_SIZE. Cependant, contrairement à Boost, vous ne vous souciez que de 2 arguments. Donc:

#define OVERLOAD(prefix, ...) CAT(prefix, VARIADIC(__VA_ARGS__))

CAT ressemblera à:

#define CAT(a, b) KITTY((a, b))
#define KITTY(par) MEOW ## par
#define MEOW(a, b) a ## b

Et VARIADIC:

#define VARIADIC(...) _VARIADIC_(__VA_ARGS__, 2, 1,)
#define _VARIADIC_(e0, e1, size, ...) size
2
OwO