Preprocessore C ++ __VA_ARGS__ numero di argomenti


99

Domanda semplice per la quale non ho trovato risposta in rete. Nelle macro di argomenti variadici, come trovare il numero di argomenti? Sto bene con il preprocessore boost, se ha la soluzione.

Se fa la differenza, sto cercando di convertire un numero variabile di argomenti macro per potenziare la sequenza, l'elenco o l'array del preprocessore per un'ulteriore rielaborazione.


Giusto per essere chiari: stai chiedendo delle macro variadiche e non delle macro usate per creare funzioni C variadiche?

2
sono gli argomenti dello stesso tipo? in tal caso, e se il tipo è noto, esiste una soluzione C standard tramite i letterali composti; se è sconosciuto, potresti usarlo __typeof__per farlo funzionare almeno su alcuni compilatori
Christoph

1
Dal momento che la discussione riguarda la sequenza del preprocessore Boost, ecc., Deve essere C ++ (motivo per cui ho riclassificato la Q - ma non sono riuscito a cambiare il titolo della domanda) ... Oops; Lo aggiusterò.
Jonathan Leffler

@JonathanLeffler Vero, Boost è una libreria C ++. Tuttavia, Boost.Preprocessor può essere utilizzato con C. AFAIK, nulla di ciò che utilizza è specifico di C ++.
Justin

Risposte:


90

Questo dipende effettivamente dal compilatore e non è supportato da nessuno standard.

Qui tuttavia hai un'implementazione di macro che fa il conteggio:

#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

... ma ora è standard in C ++ 0x e avrebbe dovuto esserlo più tempo fa perché consente un ottimo modo per proteggere le funzioni varadiche da chiamate danneggiate (cioè, puoi passare valori dopo gli elementi varadici. Questo è in realtà un modo di ottenere il conteggio che
usavo

La risposta si collega a un altro sito. Inoltre il collegamento non sembra puntare alla risposta corretta. E anche se sono riuscito a trovare la risposta prevista, sembra scadente poiché incorpora un "-1" hardcoded che verrà compilato. Esistono metodi migliori.
ceztko

2
Grazie! questo ha funzionato in Visual Studio 2013 per me: #define EXPAND(x) x #define PP_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...) N #define PP_NARG(...) EXPAND(PP_ARG_N(__VA_ARGS__, 9,8,7,6,5,4,3,2,1,0))``
mchiasson

1
PP_NARG()non restituisce 0. Le soluzioni GET_ARG_COUNT()& Y_TUPLE_SIZE()funzionano.
PSkocik

1
" PP_NARG()non restituisce 0" ... non è necessariamente un problema. Si può dire che PP_NARG() dovrebbe restituire 1 per lo stesso motivo PP_NARG(,)dovrebbe restituire 2. Rilevare 0 può effettivamente essere utile in alcuni casi, ma le soluzioni sembrano essere meno generali (richiedendo che il primo token sia incollabile; il che può o non può andare bene a seconda di cosa lo stai usando) o di implementazione specifica (come richiedere il trucco di gnu per rimuovere le virgole e incollare).
H Walters,

100

Di solito uso questa macro per trovare una serie di parametri:

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

Esempio 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;
}

È 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 in questo modo:

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

e funzionerà anche con un elenco di parametri vuoto


4
UM, non funzionerà per l'OP, ha bisogno della dimensione per BOOST_PP che gira in fase di compilazione.
Kornel Kisielewicz

5
Intelligente! Funziona anche quando sizeof(int) != sizeof(void *)?
Adam Liss

3
@Kornel Come ogni macro, viene valutata in fase di compilazione. Non ho idea di Boost, ma comunque Boost non è necessario.
qrdl

4
@Adam perché ho trasmesso {__VA_ARGS__}a int[], è solo int[], indipendentemente dal contenuto effettivo di__VA_ARGS__
qrdl

3
Soluzione elegante! Funziona in VS2017. Non ##è necessario in VS2017 poiché un vuoto __VA_ARGS__rimuoverà automaticamente qualsiasi virgola precedente.
Poby

37

Se stai usando C ++ 11 e hai bisogno del valore come costante del tempo di compilazione C ++, una soluzione molto elegante è questa:

#include <tuple>

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

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


2
Ottima soluzione! E a differenza di quanto sizeof((int[]){__VA_ARGS__})/sizeof(int)suggerito sopra, funziona anche quando gli argomenti non possono essere tutti espressi int.
Wim

Concordato. Ottima soluzione! ++.
davernator

Non funziona con i modelli, cioè NUMARGS (sum <1,2>); vedi godbolt.org/z/_AAxmL
jorgbrown

1
Io ... penso che potrebbe effettivamente essere un punto a favore, @jorgbrown, almeno nella maggior parte dei casi in cui sarebbe venuto fuori. Poiché per eseguire il conteggio si basa sul compilatore anziché sul preprocessore, fornisce il numero di argomenti visto dal compilatore, che probabilmente corrisponderà a ciò che la maggior parte dei programmatori si aspetta. Si farà causare problemi se si prevede di prendere preprocessore golosità in considerazione, però.
Justin Time - Ripristina Monica il

Ottima risposta. Puoi inserirlo in una macro#define NUM_ARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value
Richard Whitehead,

23

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 in 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");

IMHO la variante Microsoft fallisce per zero argomenti.
Vroomfondel

@ Vroomfondel la variante Microsoft funziona per zero argomenti. Il primissimo static_assert nell'esempio sopra è un test specifico per il caso senza argomenti, e l'ho appena compilato ed eseguito su Visual Studio 2017 v15.8.9.
Chris Kline

Interessante - l'utilizzo della variante Microsoft su un compilatore non Microsoft non funziona - sai cosa fa in modo diverso il preprocessore M $ che fa funzionare il codice in modo opposto? BTW ho provato C, non C ++;
Vroomfondel

Credo che sia perché MSVC è un po 'più carino riguardo alla "lunghezza zero __VA_ARGS__" (che in C ++ è tecnicamente un'estensione del compilatore (quasi universale, di fatto standard) fino a C ++ 20). La maggior parte (tutti?) Dei compilatori consente la lunghezza zero, ma soffoca sulla virgola finale se l'elenco è vuoto (e sovraccarica ##come proto- __VA_OPT__, per rimuovere la virgola in questo caso); La versione di MSVC dell'estensione non si strozza con la virgola (ma si strozza con il sovraccarico ##). Confronta MSVC unused, __VA_ARGS__con non MSVC 0, ## __VA_ARGS__; nessuno dei due è più corretto, il problema è che sono diversi.
Justin Time - Ripristina Monica il

Non sono sicuro che sia lo stesso in C, però, @Vroomfondel, dato che ho perso il mio segnalibro nella bozza più recente.
Justin Time - Ripristina Monica il

11

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

#define VA_COUNT(...) detail::va_count(__VA_ARGS__)

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

Anche questo non richiede l'inclusione <tuple>dell'intestazione.


1
"ma perché non usare semplicemente un modello variadico e sizeof ... invece (come nella mia risposta)" il c ++ è diventato un mostro. Ha troppe funzionalità e molte di esse, come i modelli variadici, vengono utilizzate raramente. Lo leggi, scrivi alcuni esempi e poi lo dimentichi. Pertanto, è difficile trovare l'idea giusta al momento giusto. Poiché la tua soluzione sembra essere un'opzione migliore della mia, lascerò che la selezione naturale funzioni e cancellerò la mia soluzione.
zdf

1
@ZDF comprensibile, ma mi capita di utilizzare costantemente modelli variadici. I miei programmi sono diventati molto più robusti da C ++ 11, e questo è uno dei motivi principali per cui. Non c'è bisogno di cancellare la tua risposta però, credo.
monkey0506

1
Non funzionerà con smth come VA_COUNT(&,^,%). Inoltre, se stai contando tramite una funzione, non vedo alcun senso nel creare una macro.
Qwertiy

Questa soluzione rimane una domanda: i parametri di VA_COUNT sono tutti identificatori che non sono ancora definiti come una variabile o qualcosa del genere, e causa l'errore "*** variabile non definita". Esiste un modo per risolvere questo problema?
ipid

7

funziona con 0 argomenti con gcc / llvm. [i link 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 utilizzare l'argomento vuoto. Probabilmente puoi aggirare il problema con qualcosa del genere

#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

L'ho testato per Visual Studio 2008 e non ha funzionato per 0 argomenti COUNT_ARGS () = 1.
user720594

Il collegamento sembra interrotto.
Jan Smrčina

collegamento fisso. VS deve fare qualcosa di diverso come al solito :). Non credo che supporteranno completamente C99 in qualunque momento presto.
user1187902

2
Ehm, ##__VA_ARGS__mangiare la virgola prima di se __VA_ARGS__è vuoto è un'estensione GCC. Non è il comportamento standard.
Finanzia la causa di Monica il

6

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.

EDIT: versione semplificata (funziona con VS2015 14.0.25431.01 Update 3 e gcc 7.4.0) fino a 100 argomenti da copiare e incollare:

#define COUNTOF(...) _COUNTOF_CAT( _COUNTOF_A, ( 0, ##__VA_ARGS__, 100,\
    99, 98, 97, 96, 95, 94, 93, 92, 91, 90,\
    89, 88, 87, 86, 85, 84, 83, 82, 81, 80,\
    79, 78, 77, 76, 75, 74, 73, 72, 71, 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 _COUNTOF_CAT( a, b ) a b
#define _COUNTOF_A( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9,\
    a10, a11, a12, a13, a14, a15, a16, a17, a18, a19,\
    a20, a21, a22, a23, a24, a25, a26, a27, a28, a29,\
    a30, a31, a32, a33, a34, a35, a36, a37, a38, a39,\
    a40, a41, a42, a43, a44, a45, a46, a47, a48, a49,\
    a50, a51, a52, a53, a54, a55, a56, a57, a58, a59,\
    a60, a61, a62, a63, a64, a65, a66, a67, a68, a69,\
    a70, a71, a72, a73, a74, a75, a76, a77, a78, a79,\
    a80, a81, a82, a83, a84, a85, a86, a87, a88, a89,\
    a90, a91, a92, a93, a94, a95, a96, a97, a98, a99,\
    a100, n, ... ) n

4
sono solo io o questo tipo di infrange le regole dell'odore del codice ..?
osirisgothra

Per me funziona con VC ++ almeno fino a VS2012, e anche GCC e clang nei miei test di base.
ThreeBit

@osirisgothra, esattamente perché puzza?
ceztko

Sebbene questa macro abbia un ampio supporto per i compilatori, non funziona con argomenti macro come una stringa, come Y_TUPLE_SIZE("Hello"), rendendola abbastanza irrealizzabile. Sono d'accordo con @osirisgothra.
ceztko

1
Questa macro può funzionare per te ma presenta gravi difetti. Ho fatto molte ricerche e ho trovato approcci più puliti che funzionano in GCC e VS. Puoi trovarli nella mia risposta a una domanda simile.
ceztko

3

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

#include <cstring>

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 funzionato per me su Godbolt per clang 4 e GCC 5.1. Questo calcolerà in fase di compilazione, ma non valuterà per il preprocessore. Quindi se stai cercando di fare qualcosa come creare un FOR_EACH , allora non funzionerà.


Questa risposta è sottovalutata. Funzionerà anche per NUMARGS(hello, world = 2, ohmy42, !@#$%^&*()-+=)!!! Ogni stringa arg non può avere altri simboli come ','però
pterodragon

Deve essere ottimizzato per i genitori, perché int count = NUMARGS( foo(1, 2) );produce 2 anziché 1. godbolt.org/z/kpBuOm
jorgbrown

Questo non funzionerà come previsto con lambda, chiamate di funzione o qualsiasi altra cosa che possa contenere virgole extra nei parametri.
Nandee

2

qui un modo semplice per contare 0 o più argomenti di VA_ARGS , il mio esempio presuppone un massimo di 5 variabili, ma puoi aggiungerne altre se vuoi.

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

Sfortunatamente l'approccio funziona in modo errato quando VA_ARGS_NUMviene utilizzato con la macro: se ho #define TEST(cioè vuoto TEST) e VA_ARGS_NUM(TEST)non restituisce 0 (zero) quando utilizzato in #if:(
AntonK

@AntonK puoi postare quello che hai fatto esattamente per favore?
elhadi dp ıpɐɥ ן ǝ

0

Puoi stringere e contare i gettoni:

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

2
Ho appena dato un'occhiata alla modifica in attesa di questa risposta: sembra che potresti avere due account. Se ti attieni a uno, sarai in grado di modificare i tuoi post senza che venga approvato.
J Richard Snape

0

Boost Preprocessor ha in realtà questo a partire da Boost 1.49, come BOOST_PP_VARIADIC_SIZE(...). Funziona fino alla taglia 64.

Sotto il cofano, è fondamentalmente la stessa della risposta di Kornel Kisielewicz .


@CarloWood Indeed. Il preprocessore non ha realmente il concetto di "zero argomenti". Quello che pensiamo come "zero argomenti" è "un argomento vuoto" nel preprocessore. Ma è risolvibile utilizzando C ++ 20 __VA_OPT__o le estensioni del compilatore per ##__VA_ARGS__rimuovere la virgola precedente, ad esempio: godbolt.org/z/X7OvnK
Justin

0

Ho trovato che le risposte qui sono ancora incomplete.

L'implementazione portatile più vicina che ho trovato da qui è: preprocessore C ++ __VA_ARGS__ numero di argomenti

Ma non funziona con gli argomenti zero nel GCC senza almeno il -std=gnu++11parametro della riga di comando.

Quindi ho deciso di unire questa soluzione con quella: https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

#define UTILITY_PP_CONCAT_(v1, v2) v1 ## v2
#define UTILITY_PP_CONCAT(v1, v2) UTILITY_PP_CONCAT_(v1, v2)

#define UTILITY_PP_CONCAT5_(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4

#define UTILITY_PP_IDENTITY_(x) x
#define UTILITY_PP_IDENTITY(x) UTILITY_PP_IDENTITY_(x)

#define UTILITY_PP_VA_ARGS_(...) __VA_ARGS__
#define UTILITY_PP_VA_ARGS(...) UTILITY_PP_VA_ARGS_(__VA_ARGS__)

#define UTILITY_PP_IDENTITY_VA_ARGS_(x, ...) x, __VA_ARGS__
#define UTILITY_PP_IDENTITY_VA_ARGS(x, ...) UTILITY_PP_IDENTITY_VA_ARGS_(x, __VA_ARGS__)

#define UTILITY_PP_IIF_0(x, ...) __VA_ARGS__
#define UTILITY_PP_IIF_1(x, ...) x
#define UTILITY_PP_IIF(c) UTILITY_PP_CONCAT_(UTILITY_PP_IIF_, c)

#define UTILITY_PP_HAS_COMMA(...) UTILITY_PP_IDENTITY(UTILITY_PP_VA_ARGS_TAIL(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0))
#define UTILITY_PP_IS_EMPTY_TRIGGER_PARENTHESIS_(...) ,

#define UTILITY_PP_IS_EMPTY(...) UTILITY_PP_IS_EMPTY_( \
    /* test if there is just one argument, eventually an empty one */ \
    UTILITY_PP_HAS_COMMA(__VA_ARGS__),                                \
    /* test if _TRIGGER_PARENTHESIS_ together with the argument adds a comma */ \
    UTILITY_PP_HAS_COMMA(UTILITY_PP_IS_EMPTY_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
    /* test if the argument together with a parenthesis adds a comma */ \
    UTILITY_PP_HAS_COMMA(__VA_ARGS__ ()),                             \
    /* test if placing it between _TRIGGER_PARENTHESIS_ and the parenthesis adds a comma */ \
    UTILITY_PP_HAS_COMMA(UTILITY_PP_IS_EMPTY_TRIGGER_PARENTHESIS_ __VA_ARGS__ ()))

#define UTILITY_PP_IS_EMPTY_(_0, _1, _2, _3) UTILITY_PP_HAS_COMMA(UTILITY_PP_CONCAT5_(UTILITY_PP_IS_EMPTY_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define UTILITY_PP_IS_EMPTY_IS_EMPTY_CASE_0001 ,

#define UTILITY_PP_VA_ARGS_SIZE(...) UTILITY_PP_IIF(UTILITY_PP_IS_EMPTY(__VA_ARGS__))(0, UTILITY_PP_VA_ARGS_SIZE_(__VA_ARGS__, UTILITY_PP_VA_ARGS_SEQ64()))
#define UTILITY_PP_VA_ARGS_SIZE_(...) UTILITY_PP_IDENTITY(UTILITY_PP_VA_ARGS_TAIL(__VA_ARGS__))

#define UTILITY_PP_VA_ARGS_TAIL(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14, x, ...) x
#define UTILITY_PP_VA_ARGS_SEQ64() 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0

#define EATER0(...)
#define EATER1(...) ,
#define EATER2(...) (/*empty*/)
#define EATER3(...) (/*empty*/),
#define EATER4(...) EATER1
#define EATER5(...) EATER2
#define MAC0() ()
#define MAC1(x) ()
#define MACV(...) ()
#define MAC2(x,y) whatever

static_assert(UTILITY_PP_VA_ARGS_SIZE() == 0, "1");
static_assert(UTILITY_PP_VA_ARGS_SIZE(/*comment*/) == 0, "2");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a) == 1, "3");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a, b) == 2, "4");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a, b, c) == 3, "5");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a, b, c, d) == 4, "6");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a, b, c, d, e) == 5, "7");
static_assert(UTILITY_PP_VA_ARGS_SIZE((void)) == 1, "8");
static_assert(UTILITY_PP_VA_ARGS_SIZE((void), b, c, d) == 4, "9");
static_assert(UTILITY_PP_VA_ARGS_SIZE(UTILITY_PP_IS_EMPTY_TRIGGER_PARENTHESIS_) == 1, "10");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER0) == 1, "11");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER1) == 1, "12");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER2) == 1, "13");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER3) == 1, "14");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER4) == 1, "15");
static_assert(UTILITY_PP_VA_ARGS_SIZE(MAC0) == 1, "16");
// a warning in msvc
static_assert(UTILITY_PP_VA_ARGS_SIZE(MAC1) == 1, "17");
static_assert(UTILITY_PP_VA_ARGS_SIZE(MACV) == 1, "18");
// This one will fail because MAC2 is not called correctly
//static_assert(UTILITY_PP_VA_ARGS_SIZE(MAC2) == 1, "19");

https://godbolt.org/z/3idaKd

  • c++11, msvc 2015, gcc 4.7.1,clang 3.0
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.