c++ c c-preprocessor variadic-macros

Preprocesador C++__VA_ARGS__ cantidad de argumentos



c-preprocessor variadic-macros (9)

Con la extensión msvc:

#define Y_TUPLE_SIZE(...) Y_TUPLE_SIZE_II((Y_TUPLE_SIZE_PREFIX_ ## __VA_ARGS__ ## _Y_TUPLE_SIZE_POSTFIX,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)) #define Y_TUPLE_SIZE_II(__args) Y_TUPLE_SIZE_I __args #define Y_TUPLE_SIZE_PREFIX__Y_TUPLE_SIZE_POSTFIX ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0 #define Y_TUPLE_SIZE_I(__p0,__p1,__p2,__p3,__p4,__p5,__p6,__p7,__p8,__p9,__p10,__p11,__p12,__p13,__p14,__p15,__p16,__p17,__p18,__p19,__p20,__p21,__p22,__p23,__p24,__p25,__p26,__p27,__p28,__p29,__p30,__p31,__n,...) __n

Funciona para 0 - 32 argumentos. Este límite se puede extender fácilmente.

Pregunta simple para la cual no pude encontrar respuesta en la red. En macros de argumentos variados, ¿cómo encontrar el número de argumentos? Estoy de acuerdo con el preprocesador de refuerzo, si tiene la solución.

Si hace una diferencia, estoy tratando de convertir un número variable de argumentos de macro para aumentar la secuencia del preprocesador, la lista o la matriz para su posterior reprocesamiento.


Esto es realmente dependiente del compilador, y no es compatible con ningún estándar.

Sin embargo, aquí tiene una implementación macro que hace el recuento:

#define PP_NARG(...) / PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) #define PP_NARG_(...) / PP_ARG_N(__VA_ARGS__) #define PP_ARG_N( / _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, / _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, / _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, / _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, / _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, / _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, / _61,_62,_63,N,...) N #define PP_RSEQ_N() / 63,62,61,60, / 59,58,57,56,55,54,53,52,51,50, / 49,48,47,46,45,44,43,42,41,40, / 39,38,37,36,35,34,33,32,31,30, / 29,28,27,26,25,24,23,22,21,20, / 19,18,17,16,15,14,13,12,11,10, / 9,8,7,6,5,4,3,2,1,0 /* Some test cases */ PP_NARG(A) -> 1 PP_NARG(A,B) -> 2 PP_NARG(A,B,C) -> 3 PP_NARG(A,B,C,D) -> 4 PP_NARG(A,B,C,D,E) -> 5 PP_NARG(1,2,3,4,5,6,7,8,9,0, 1,2,3,4,5,6,7,8,9,0, 1,2,3,4,5,6,7,8,9,0, 1,2,3,4,5,6,7,8,9,0, 1,2,3,4,5,6,7,8,9,0, 1,2,3,4,5,6,7,8,9,0, 1,2,3) -> 63


Existen algunas soluciones de C ++ 11 para encontrar el número de argumentos en tiempo de compilación, pero me sorprende ver que nadie ha sugerido algo tan simple como:

#define VA_COUNT(...) detail::va_count(__VA_ARGS__) namespace detail { template<typename ...Args> constexpr std::size_t va_count(Args&&...) { return sizeof...(Args); } }

Esto tampoco requiere la inclusión del encabezado <tuple> .


Normalmente utilizo esta macro para encontrar una serie de parámetros:

#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))

Ejemplo completo:

#include <stdio.h> #include <string.h> #include <stdarg.h> #define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int)) #define SUM(...) (sum(NUMARGS(__VA_ARGS__), __VA_ARGS__)) void sum(int numargs, ...); int main(int argc, char *argv[]) { SUM(1); SUM(1, 2); SUM(1, 2, 3); SUM(1, 2, 3, 4); return 1; } void sum(int numargs, ...) { int total = 0; va_list ap; printf("sum() called with %d params:", numargs); va_start(ap, numargs); while (numargs--) total += va_arg(ap, int); va_end(ap); printf(" %d/n", total); return; }

Es un código C99 completamente válido. Sin embargo, tiene un inconveniente: no se puede invocar la macro SUM() sin params, pero GCC tiene una solución para ello - ver here .

Entonces, en el caso de GCC necesita definir macros como este:

#define NUMARGS(...) (sizeof((int[]){0, ##__VA_ARGS__})/sizeof(int)-1) #define SUM(...) sum(NUMARGS(__VA_ARGS__), ##__VA_ARGS__)

y funcionará incluso con la lista de parámetros vacía


Para su comodidad, aquí hay una implementación que funciona para 0 a 70 argumentos, y funciona en Visual Studio, GCC y Clang . Creo que funcionará en Visual Studio 2010 y posterior, pero solo lo he probado en VS2013.

#ifdef _MSC_VER // Microsoft compilers # define GET_ARG_COUNT(...) INTERNAL_EXPAND_ARGS_PRIVATE(INTERNAL_ARGS_AUGMENTER(__VA_ARGS__)) # define INTERNAL_ARGS_AUGMENTER(...) unused, __VA_ARGS__ # define INTERNAL_EXPAND(x) x # define INTERNAL_EXPAND_ARGS_PRIVATE(...) INTERNAL_EXPAND(INTERNAL_GET_ARG_COUNT_PRIVATE(__VA_ARGS__, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) # define INTERNAL_GET_ARG_COUNT_PRIVATE(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, _10_, _11_, _12_, _13_, _14_, _15_, _16_, _17_, _18_, _19_, _20_, _21_, _22_, _23_, _24_, _25_, _26_, _27_, _28_, _29_, _30_, _31_, _32_, _33_, _34_, _35_, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, count, ...) count #else // Non-Microsoft compilers # define GET_ARG_COUNT(...) INTERNAL_GET_ARG_COUNT_PRIVATE(0, ## __VA_ARGS__, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) # define INTERNAL_GET_ARG_COUNT_PRIVATE(_0, _1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, _10_, _11_, _12_, _13_, _14_, _15_, _16_, _17_, _18_, _19_, _20_, _21_, _22_, _23_, _24_, _25_, _26_, _27_, _28_, _29_, _30_, _31_, _32_, _33_, _34_, _35_, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, count, ...) count #endif static_assert(GET_ARG_COUNT() == 0, "GET_ARG_COUNT() failed for 0 arguments"); static_assert(GET_ARG_COUNT(1) == 1, "GET_ARG_COUNT() failed for 1 argument"); static_assert(GET_ARG_COUNT(1,2) == 2, "GET_ARG_COUNT() failed for 2 arguments"); static_assert(GET_ARG_COUNT(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70) == 70, "GET_ARG_COUNT() failed for 70 arguments");


Puede codificar y contar tokens:

int countArgs(char *args) { int result = 0; int i = 0; while(isspace(args[i])) ++i; if(args[i]) ++result; while(args[i]) { if(args[i]=='','') ++result; else if(args[i]==''/''') i+=2; else if(args[i]==''/"'') { while(args[i]) { if(args[i+1]==''/"'' && args[i]!=''//') { ++i; break; } ++i; } } ++i; } return result; } #define MACRO(...) / { / int count = countArgs(#__VA_ARGS__); / printf("NUM ARGS: %d/n",count); / }


Si está utilizando C ++ 11 y necesita el valor como una constante en tiempo de compilación C ++, una solución muy elegante es esta:

#include <tuple> #define MACRO(...) / std::cout << "num args: " / << std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value / << std::endl;

Tenga en cuenta: el recuento se realiza completamente en tiempo de compilación, y el valor se puede usar siempre que se requiera un número de compilación en tiempo, por ejemplo como un parámetro de plantilla para std :: array.


en este documento, una manera simple de contar 0 o más argumentos de VA_ARGS , mi ejemplo supone un máximo de 5 variables, pero puede agregar más si lo desea.

#define VA_ARGS_NUM_PRIV(P1, P2, P3, P4, P5, P6, Pn, ...) Pn #define VA_ARGS_NUM(...) VA_ARGS_NUM_PRIV(-1, ##__VA_ARGS__, 5, 4, 3, 2, 1, 0) VA_ARGS_NUM() ==> 0 VA_ARGS_NUM(19) ==> 1 VA_ARGS_NUM(9, 10) ==> 2 ...


esto funciona con 0 argumentos con gcc / llvm. [los enlaces son tontos]

/* * we need a comma at the start for ##_VA_ARGS__ to consume then * the arguments are pushed out in such a way that ''cnt'' ends up with * the right count. */ #define COUNT_ARGS(...) COUNT_ARGS_(,##__VA_ARGS__,6,5,4,3,2,1,0) #define COUNT_ARGS_(z,a,b,c,d,e,f,cnt,...) cnt #define C_ASSERT(test) / switch(0) {/ case 0:/ case test:;/ } int main() { C_ASSERT(0 == COUNT_ARGS()); C_ASSERT(1 == COUNT_ARGS(a)); C_ASSERT(2 == COUNT_ARGS(a,b)); C_ASSERT(3 == COUNT_ARGS(a,b,c)); C_ASSERT(4 == COUNT_ARGS(a,b,c,d)); C_ASSERT(5 == COUNT_ARGS(a,b,c,d,e)); C_ASSERT(6 == COUNT_ARGS(a,b,c,d,e,f)); return 0; }

Visual Studio parece ignorar el operador ## utilizado para consumir el argumento vacío. Probablemente puedas evitarlo con algo como

#define CNT_ COUNT_ARGS #define PASTE(x,y) PASTE_(x,y) #define PASTE_(x,y) x ## y #define CNT(...) PASTE(ARGVS,PASTE(CNT_(__VA_ARGS__),CNT_(1,##__VA_ARGS__))) //you know its 0 if its 11 or 01 #define ARGVS11 0 #define ARGVS01 0 #define ARGVS12 1 #define ARGVS23 2 #define ARGVS34 3