Question

Simple question pour laquelle je ne pouvais pas trouver réponse sur le net. Dans les macros argument variadique, comment trouver le nombre d'arguments? Je suis d'accord avec préprocesseur boost, si elle a la solution.

Si cela fait une différence, je suis en train de convertir un nombre variable d'arguments macro pour stimuler séquence préprocesseur, liste ou un tableau pour plus retraitement.

Était-ce utile?

La solution

Ceci est en fait dépend du compilateur, et non pris en charge par aucune norme.

Ici cependant, vous avez un macro la mise en œuvre qui fait le compte:

#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

Autres conseils

Je l'habitude d'utiliser cette macro pour trouver un certain nombre de params:

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

Exemple complet:

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

Il est tout à fait valide le code C99. Il a un inconvénient, bien que - vous ne pouvez pas invoquer la SUM() macro sans params, mais GCC a une solution à ce problème - voir ici .

Ainsi, en cas de GCC vous devez définir des macros comme ceci:

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

et il fonctionne même avec la liste des paramètres vide

Si vous utilisez C ++ 11, et vous avez besoin de la valeur en tant que C ++ constante de compilation, une solution très élégante est celle-ci:

#include <tuple>

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

S'il vous plaît noter:. Le décompte arrive tout au moment de la compilation et la valeur peut être utilisée chaque fois entier la compilation de temps est nécessaire, par exemple en tant que paramètre de modèle à std :: tableau

Pour plus de commodité, voici une implémentation qui fonctionne pour 0 à 70 arguments, et fonctionne dans Visual Studio, GCC, et Clang . Je crois que cela fonctionnera dans Visual Studio 2010 et plus tard, mais n'ai testé dans 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");

Il y a quelques C ++ 11 solutions pour trouver le nombre d'arguments à la compilation, mais je suis surpris de voir que personne n'a suggéré quoi que ce soit aussi simple que:

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

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

Cela ne nécessite pas l'inclusion de l'en-tête de <tuple> soit.

Avec l'extension 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

Travaux de 0 - 32 arguments. Cette limite peut être facilement étendu.

cela fonctionne avec 0 arguments avec gcc / LLVM. [Liens sont muets]

/*
 * 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 semble ignorer l'opérateur ## utilisé pour consommer l'argument vide. Vous pouvez probablement contourner cela avec quelque chose comme

#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

présente un moyen simple de compter 0 ou plusieurs arguments de VA_ARGS , mon suppose un maximum exemple de 5 variables, mais vous pouvez ajouter plus si vous voulez.

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

Je suppose que chaque argument VA_ARGS seront séparés par des virgules. Si donc je pense que cela devrait fonctionner comme un moyen assez propre pour le faire.

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

A travaillé pour moi sur Godbolt pour clang 4 et GCC 5.1. Cela calcule au moment de la compilation, mais n'évaluera le préprocesseur. Donc, si vous essayez de faire quelque chose comme faire un for_each , cela ne fonctionnera pas.

Vous pouvez stringify et compter les jetons:

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

Boost préprocesseur a fait cela comme Boost 1,49, comme BOOST_PP_VARIADIC_SIZE(...) . Il fonctionne jusqu'à la taille 64.

Sous le capot, il est essentiellement le même que la réponse de Kornel Kisielewicz .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top