Numero di argomenti del preprocessore C ++ __VA_ARGS__

Semplice domanda per la quale non ho potuto trovare risposta in rete. In macro argomento variadico, come trovare il numero di argomenti? Sto bene con il preprocessore boost, se ha la soluzione.

Se fa la differenza, sto provando a convertire il numero variabile di argomenti macro per migliorare la sequenza, l’elenco o l’array del preprocessore per ulteriori rielaborazioni.

Questo dipende dal compilatore e non è supportato da alcuno standard.

Qui però hai un’implementazione macro che conta:

 #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 

Di solito uso questa macro per trovare un numero di parametri:

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

Esempio completo:

 #include  #include  #include  #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; } 

È un codice C99 completamente valido. Ha uno svantaggio, però – non puoi invocare la macro SUM() senza parametri, ma GCC ha una soluzione – vedi qui .

Quindi in caso di GCC è necessario definire macro come questa:

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

e funzionerà anche con la lista dei parametri vuota

Se si utilizza C ++ 11 e si ha bisogno del valore come costante di compilazione C ++, una soluzione molto elegante è questa:

 #include  #define MACRO(...) \ std::cout << "num args: " \ << std::tuple_size::value \ << std::endl; 

Nota: il conteggio avviene interamente al momento della compilazione e il valore può essere utilizzato ogni volta che è richiesto un numero intero in fase di compilazione, ad esempio come parametro del modello su std :: array.

Per comodità, ecco un’implementazione che funziona da 0 a 70 argomenti e funziona in Visual Studio, GCC e Clang . Credo che funzionerà in Visual Studio 2010 e versioni successive, ma l’ho testato solo su 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"); 

Con estensione 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 

Funziona per 0 – 32 argomenti. Questo limite può essere facilmente esteso.

questo funziona con 0 argomenti con gcc / llvm. [i collegamenti sono stupidi]

 /* * 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 sembra ignorare l’operatore ## utilizzato per consumare l’argomento vuoto. Probabilmente puoi aggirare il problema con qualcosa di simile

 #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 

Ci sono alcune soluzioni C ++ 11 per trovare il numero di argomenti in fase di compilazione, ma sono sorpreso nel vedere che nessuno ha suggerito qualcosa di così semplice come:

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

Ciò non richiede l’inclusione dell’intestazione .

in questo modo un modo semplice per contare 0 o più argomenti di VA_ARGS , il mio esempio assume un massimo di 5 variabili, ma è ansible aggiungerne altre se lo si desidera.

 #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 ... 

Puoi stringere e contare i token:

 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); \ } 

Suppongo che ogni argomento di VA_ARGS sarà separato da virgole. Se è così penso che questo dovrebbe funzionare come un modo abbastanza pulito per farlo.

 #include  constexpr int CountOccurances(const char* str, char c) { return str[0] == char(0) ? 0 : (str[0] == c) + CountOccurances(str+1, c); } #define NUMARGS(...) (CountOccurances(#__VA_ARGS__, ',') + 1) int main(){ static_assert(NUMARGS(hello, world) == 2, ":(") ; return 0; } 

Ha lavorato per me su Godbolt per clang 4 e GCC 5.1. Questo verrà calcolato al momento della compilazione, ma non verrà valutato per il preprocessore. Quindi se stai provando a fare qualcosa come fare un FOR_EACH , allora questo non funzionerà.