सी ++ प्रीप्रोसेसर __VA_ARGS__ तर्कों की संख्या


99

सरल प्रश्न जिसके लिए मुझे नेट पर उत्तर नहीं मिला। वैरेडिक तर्क मैक्रोज़ में, तर्कों की संख्या कैसे ज्ञात करें? मैं बढ़ावा देने के साथ ठीक हूँ, अगर यह समाधान है।

यदि इससे कोई फ़र्क पड़ता है, तो मैं आगे पुनरावृत्ति के लिए प्रीप्रोसेसर अनुक्रम, सूची, या सरणी को बढ़ावा देने के लिए मैक्रो तर्कों की चर संख्या को बदलने की कोशिश कर रहा हूं।


बस स्पष्ट होने के लिए - आप वैरेडिक मैक्रोज़ के बारे में पूछ रहे हैं, न कि मैक्रो वैरेडिक सी फ़ंक्शन बनाने के लिए उपयोग किया जाता है?

2
क्या एक ही प्रकार के तर्क हैं? यदि ऐसा है, और यदि प्रकार ज्ञात है, तो यौगिक शाब्दिकों के माध्यम से एक मानक सी समाधान है; यदि यह अज्ञात है, तो आप __typeof__इसे कम से कम कुछ संकलक पर काम करने के लिए उपयोग कर सकते हैं
क्रिस्टोफ

1
चूंकि चर्चा बूस्ट प्रीप्रोसेसर अनुक्रम आदि के बारे में है, इसलिए इसे C ++ होना चाहिए (यही कारण है कि मैंने Q को फिर से बताया - लेकिन प्रश्न शीर्षक को बदलने में विफल रहा) ... उफ़; मैं ठीक कर दूँगा।
जोनाथन लेफ्लर

@JonathanLeffler True, Boost एक C ++ लाइब्रेरी है। हालांकि, Boost.Preprocessor का उपयोग C. AFAIK के साथ किया जा सकता है, इसका उपयोग करने वाला कुछ भी C ++ विशिष्ट नहीं है।
जस्टिन

जवाबों:


90

यह वास्तव में संकलक पर निर्भर है, और किसी भी मानक द्वारा समर्थित नहीं है।

हालांकि आपके पास एक मैक्रो कार्यान्वयन है जो गणना करता है:

#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

.... लेकिन अब C ++ 0x में मानक है और अब से पहले होना चाहिए क्योंकि यह दूषित कॉल से वैरडीक कार्यों को संरक्षित करने का एक शानदार तरीका है (यानी, आप वैरेडिक आइटम के बाद मान पास कर सकते हैं। यह वास्तव में एक तरीका है। मेरे द्वारा उपयोग की जाने वाली गिनती प्राप्त करने के लिए, लेकिन मुझे लगता है कि
साइज़ोफ़

उत्तर किसी अन्य साइट से लिंक करता है। इसके अलावा लिंक सही उत्तर की ओर इशारा नहीं करता है। और यहां तक ​​कि अगर मैं इच्छित उत्तर खोजने में कामयाब रहा, तो यह एक खराब लगता है क्योंकि यह एक हार्डकोड "-1" एम्बेड करता है जिसे संकलित किया जाएगा। बेहतर तरीके हैं।
सीटेकोको

2
धन्यवाद! इसने मेरे लिए विज़ुअल स्टूडियो 2013 में काम किया: #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))`` `
मोचीसन

1
PP_NARG()वापस करने में विफल रहता है 0. GET_ARG_COUNT()और Y_TUPLE_SIZE()समाधान काम करते हैं।
PSkocik

1
" PP_NARG()वापस करने में विफल रहता है" ... जरूरी नहीं कि एक समस्या है। एक कह सकते हैं कि PP_NARG() होना चाहिए एक ही कारण के लिए 1 लौट PP_NARG(,)2. लौटना चाहिए का पता लगाने 0 वास्तव में कुछ मामलों में उपयोगी हो सकता है, लेकिन समाधान या तो कम सामान्य होने लगते हैं (की आवश्यकता होती है कि पहले टोकन pasteable हो, जो हो सकता है या नहीं हो सकता है हो सकता है ठीक इस पर निर्भर करता है कि आप इसके लिए क्या उपयोग कर रहे हैं), या कार्यान्वयन विशिष्ट (जैसे कि गन्नू का कॉमा-रिमूवल-पेस्ट ट्रिक)।
एच वाल्टर

100

मैं आमतौर पर इस मैक्रो का उपयोग कई पार्म्स को खोजने के लिए करता हूं:

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

पूर्ण उदाहरण:

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

यह पूरी तरह से वैध C99 कोड है। इसकी एक खामी है, हालांकि - आप मैक्रों को SUM()बिना परम के आमंत्रित नहीं कर सकते हैं , लेकिन जीसीसी के पास इसका समाधान है - यहां देखें ।

तो जीसीसी के मामले में आपको मैक्रोज़ को इस तरह परिभाषित करने की आवश्यकता है:

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

और यह खाली पैरामीटर सूची के साथ भी काम करेगा


4
उम, यह ओपी के लिए काम नहीं करेगा, उसे BOOST_PP के लिए आकार की आवश्यकता है जो संकलन समय पर चलता है।
कोर्नेल किलीविलेज़

5
चतुर! क्या यह भी काम करता है sizeof(int) != sizeof(void *)?
एडम लिस

3
@ कोर्नेल किसी भी स्थूल की तरह, इसका संकलन समय पर किया जाता है। मुझे बूस्ट के बारे में कोई जानकारी नहीं है, लेकिन वैसे भी बूस्ट की जरूरत नहीं है।
२३:४४ बजे क़र्डल

4
@ एडम क्योंकि मैं कास्ट {__VA_ARGS__}करता हूं int[], यह सिर्फ int[]वास्तविक सामग्री की परवाह किए बिना है__VA_ARGS__
qrdl

3
सुरुचिपूर्ण समाधान! VS2017 में काम करता है। ##एक खाली के रूप में VS2017 में की जरूरत नहीं है __VA_ARGS__स्वचालित रूप से किसी भी पूर्ववर्ती अल्पविराम निकाल देंगे।
पोबी

37

यदि आप C ++ 11 का उपयोग कर रहे हैं, और आपको C ++ संकलन-समय स्थिर के रूप में मान की आवश्यकता है, तो एक बहुत ही सुंदर समाधान यह है:

#include <tuple>

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

कृपया ध्यान दें: गणना पूरी तरह से संकलन समय पर होती है, और जब भी कंपाइल-टाइम पूर्णांक की आवश्यकता होती है, तो मूल्य का उपयोग किया जा सकता है, उदाहरण के लिए टेम्पलेट पैरामीटर के रूप में std :: array।


2
महान समाधान! और sizeof((int[]){__VA_ARGS__})/sizeof(int)ऊपर दिए गए सुझाव के विपरीत , यह तब भी काम करता है जब तर्क सभी को नहीं दिए जा सकते int
विमन

माना। महान समाधान! ++।
डेवनेटर

टेम्पलेट्स के साथ काम नहीं करता है, अर्थात NUMARGS (योग <1,2>); देख godbolt.org/z/_AAxmL
jorgbrown

1
मैं ... सोचता हूं कि यह वास्तव में इसके पक्ष में एक बिंदु हो सकता है, @jorgbrown, कम से कम ज्यादातर मामलों में जहां यह ऊपर आएगा। चूंकि यह गिनती करने के लिए प्रीप्रोसेसर के बजाय संकलक पर निर्भर करता है, यह संकलक द्वारा देखे गए तर्कों की संख्या देता है, जो कि संभवतः सबसे अधिक प्रोग्रामर के साथ मेल खाते हैं। यह होगा हालांकि परेशानी पैदा करता है, तो आप इसे ध्यान में पूर्वप्रक्रमक लालच अपनाए जाने की अपेक्षा,।
जस्टिन टाइम -

शानदार जवाब। आप इसे एक मैक्रो#define NUM_ARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value
रिचर्ड व्हाइटहेड

23

सुविधा के लिए, यहां एक कार्यान्वयन है जो 0 से 70 तर्कों के लिए काम करता है, और विज़ुअल स्टूडियो, जीसीसी और क्लैंग में काम करता है । मेरा मानना ​​है कि यह विज़ुअल स्टूडियो 2010 और बाद में काम करेगा, लेकिन केवल वीएस2013 में इसका परीक्षण किया है।

#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 Microsoft संस्करण शून्य तर्कों के लिए विफल रहता है।
Vroomfondel

@Vroomfondel Microsoft संस्करण शून्य तर्क के लिए काम करता है। ऊपर दिए गए उदाहरण में बहुत पहले static_assert शून्य-तर्क मामले के लिए एक विशिष्ट परीक्षण है, और मैंने अभी इसे विजुअल स्टूडियो 2017 v15.8.9 पर संकलित और चलाया है।
क्रिस क्लाइन

दिलचस्प - गैर-Microsoft कंपाइलर पर Microsoft संस्करण का उपयोग करना काम नहीं करता है - क्या आप जानते हैं कि एम $ प्रीप्रोसेसर अलग तरीके से करता है जो कोड के विपरीत काम करता है? BTW मैंने C की कोशिश की, C ++ की नहीं;
वूमफोंडेल

मेरा मानना ​​है कि क्योंकि MSVC "शून्य-लंबाई __VA_ARGS__" के बारे में थोड़ा अच्छा है (जो C ++ में है, तकनीकी रूप से एक (nigh-Universal, de facto standard) कंपाइलर एक्सटेंशन है जो C ++ 20 तक है। अधिकांश (? सब) compilers शून्य लंबाई की अनुमति देते हैं, लेकिन अनुगामी अल्पविराम पर गला घोंटना यदि सूची है खाली (और अधिभार ##एक proto- के रूप में __VA_OPT__, इस मामले में अल्पविराम दूर करने के लिए); एक्सटेंशन का MSVC का संस्करण सिर्फ अल्पविराम (लेकिन अतिभारित पर गला घोंटना ) पर चोक नहीं करेगा## । MSVC की तुलना unused, __VA_ARGS__गैर- MSVC से करें 0, ## __VA_ARGS__; न तो अधिक सही है, समस्या यह है कि वे अलग हैं।
जस्टिन टाइम -

मुझे यकीन नहीं है कि यह सी में ही है, हालांकि, @Vroomfondel, क्योंकि मैंने अपने बुकमार्क को हाल ही के ड्राफ्ट में खो दिया है।
जस्टिन टाइम -

11

संकलन-समय पर तर्कों की संख्या का पता लगाने के लिए कुछ C ++ 11 समाधान हैं, लेकिन मुझे यह देखकर आश्चर्य होता है कि किसी ने कुछ भी इतना सरल नहीं सुझाया है:

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

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

इसके लिए <tuple>हेडर को शामिल करने की आवश्यकता नहीं है ।


1
"लेकिन क्यों न केवल एक वैरेडिक टेम्प्लेट और साइज़ोफ़ का उपयोग करें ... इसके बजाय (जैसा कि मेरे अपने उत्तर में है)" सी ++ एक राक्षस बन गया है। इसकी बहुत सी विशेषताएं हैं और उनमें से कई, जैसे कि वैरेडिक टेम्प्लेट, शायद ही कभी उपयोग किए जाते हैं। आप इसके बारे में पढ़ते हैं, आप कुछ उदाहरण लिखते हैं और फिर आप इसे भूल जाते हैं। इसलिए, सही समय पर सही विचार के साथ आना मुश्किल है। चूँकि आपका समाधान खदान से बेहतर विकल्प लगता है, इसलिए मैं प्राकृतिक चयन को काम करने दूंगा और मैं अपना समाधान हटा दूंगा।
zdf

1
@ GBPF समझ में आता है, लेकिन मैं लगातार वैरेडिक टेम्प्लेट का उपयोग करता हूं। C ++ 11 के बाद से मेरे कार्यक्रम बहुत अधिक मजबूत हो गए हैं, और यह मुख्य कारणों में से एक है। मुझे लगता है कि आपके उत्तर को हटाने की आवश्यकता नहीं है।
बंदर 0506

1
यह जैसे smth के साथ काम नहीं करेगा VA_COUNT(&,^,%)। इसके अलावा, यदि आप एक अंत्येष्टि के माध्यम से गिनती कर रहे हैं, तो मुझे मैक्रो बनाने में कोई कमी नहीं दिखती है।
क्वर्टी

यह समाधान एक प्रश्न बना हुआ है: VA_COUNT के पैरामीटर सभी पहचानकर्ता हैं जिन्हें अभी तक एक चर या कुछ के रूप में परिभाषित नहीं किया गया है, और यह त्रुटि का कारण बनता है '*** चर परिभाषित नहीं है'। क्या इसको ठीक करने का कोई तरीका है?
ipid

7

यह gcc / llvm के साथ 0 तर्कों के साथ काम करता है। [लिंक गूंगा हैं]

/*
 * 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;
}

दृश्य स्टूडियो खाली तर्क का उपभोग करने के लिए उपयोग किए जाने वाले ## ऑपरेटर की अनदेखी कर रहा है। आप शायद कुछ के साथ ऐसा कर सकते हैं

#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

मैंने इसे विजुअल स्टूडियो 2008 के लिए परीक्षण किया और यह 0 तर्कों के लिए काम नहीं किया COUNT_ARGS () = 1.
user720594

लिंक टूटा हुआ लगता है।
जनवरी Smrčina

निश्चित लिंक VS हमेशा की तरह कुछ अलग करना चाहिए :)। मुझे नहीं लगता कि वे जल्द ही किसी भी समय पूरी तरह से C99 का समर्थन करने जा रहे हैं।
user1187902

2
ईआर, खाली ##__VA_ARGS__होने से पहले कॉमा खाने से __VA_ARGS__जीसीसी विस्तार होता है। यह मानक व्यवहार नहीं है।
निधि मोनिका का मुकदमा

6

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

0 के लिए काम करता है - 32 तर्क। इस सीमा को आसानी से बढ़ाया जा सकता है।

संपादित करें: सरलीकृत संस्करण (प्रतिलिपि और पेस्ट करने के लिए 100 तर्कों के लिए VS2015 14.0.25431.01 अपडेट 3 और जीसीसी 7.4.0 में काम करता है):

#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
यह सिर्फ मुझे है या इस थोड़े कोड गंध नियमों को तोड़ने करता है ..?
ओसिरिसगोथरा

यह मेरे लिए VC ++ के साथ कम से कम VS2012, और GCC के साथ काम करता है और मेरे मूल परीक्षण में भी शामिल है।
थ्रीबीट

@osirisgothra, क्यों यह बदबू आ रही है?
22

जबकि इस मैक्रो में व्यापक संकलक समर्थन है, यह मैक्रो तर्कों के साथ काम नहीं करता है जैसे कि एक स्ट्रिंग, जैसे Y_TUPLE_SIZE("Hello"), यह काफी अचूक बनाता है। मैं @osirisgothra से सहमत हूं।
सेप्टेंको

1
यह मैक्रो आपके लिए काम कर सकता है लेकिन इसमें गंभीर दोष हैं। मैंने काफी शोध किया और पाया कि क्लीनर GCC और VS में काम करते हैं। आप उन्हें मेरे सवाल के जवाब में पा सकते हैं ।
Ceztko

3

मैं मान रहा हूं कि VA_ARGS के लिए प्रत्येक तर्क अलग हो जाएगा। यदि ऐसा है तो मुझे लगता है कि इसे ऐसा करने के लिए एक सुंदर तरीके के रूप में काम करना चाहिए।

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

क्लैंग 4 और जीसीसी 5.1 के लिए गॉडबोल्ट पर मेरे लिए काम किया। यह संकलन समय पर गणना करेगा, लेकिन प्रीप्रोसेसर के लिए मूल्यांकन नहीं करेगा। इसलिए यदि आप कुछ बनाने की कोशिश कर रहे हैं जैसे कि FOR_EACH बनाना , तो यह काम नहीं करेगा।


यह उत्तर रेखांकित है। यह भी के लिए काम करेंगे NUMARGS(hello, world = 2, ohmy42, !@#$%^&*()-+=)!!! प्रत्येक आर्ग स्ट्रिंग कुछ अन्य प्रतीकों की तरह नहीं हो सकता है ',', हालांकि
pterodragon

Parens के लिए ट्विक करने की आवश्यकता है, क्योंकि int count = NUMARGS( foo(1, 2) );1 के बजाय 2 का उत्पादन होता है। Godbolt.org/z/kpBuOm
jorgbrown

यह लैम्ब्डा, फ़ंक्शन कॉल या कुछ और के साथ अपेक्षा के अनुरूप काम नहीं करेगा जिसमें पैरामीटर में अतिरिक्त अल्पविराम हो सकते हैं।
नंदी अष्टक

2

इसमें VA_ARGS के 0 या अधिक तर्कों को गिनने का एक सरल तरीका है , मेरी छूट अधिकतम 5 चर मानती है, लेकिन आप चाहें तो अधिक जोड़ सकते हैं।

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

दुर्भाग्य से दृष्टिकोण गलत तरीके से काम करता है जब VA_ARGS_NUMमैक्रो के साथ प्रयोग किया जाता है: अगर मेरे पास #define TEST(यानी खाली है TEST) और VA_ARGS_NUM(TEST)0 में वापस नहीं आता है (शून्य) #if:( में इस्तेमाल किया जाता है :
एंटोन मार्क

@AntonK क्या आप पोस्ट कर सकते हैं कि आपने क्या किया है?
इलाहदी डीपी ıpɐɥ had '

0

आप सख्ती कर सकते हैं और टोकन गिन सकते हैं:

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
बस इस उत्तर पर लंबित संपादन पर एक नज़र थी - ऐसा लगता है कि आपको दो खाते मिल सकते हैं। यदि आप एक से चिपके रहते हैं, तो आप बिना स्वीकृति के अपनी पोस्ट संपादित कर सकेंगे।
जे रिचर्ड स्नेप

0

बूस्ट प्रीप्रोसेसर वास्तव में बूस्ट 1.49 के रूप में है BOOST_PP_VARIADIC_SIZE(...)। यह 64 के आकार तक काम करता है।

हुड के तहत, यह मूल रूप से Kornel Kisielewicz के उत्तर के समान है


@CarloWood वास्तव में। प्रीप्रोसेसर के पास वास्तव में "शून्य तर्क" की अवधारणा नहीं है। "शून्य तर्क" के रूप में हम जो सोचते हैं वह पूर्वप्रोसेसर में "एक खाली तर्क" है। लेकिन यह C ++ 20 __VA_OPT__या ##__VA_ARGS__पिछले कॉमा को हटाने के लिए संकलक एक्सटेंशन का उपयोग करने के लिए ठीक करने योग्य है, उदाहरण के लिए: godbolt.org/z/X7OvnK
जस्टिन

0

मुझे लगता है कि उत्तर अभी भी अधूरे हैं।

सबसे करीबी पोर्टेबल कार्यान्वयन जो मैंने यहाँ से पाया है वह है: C ++ प्रीप्रोसेसर __VA_ARGS__ तर्कों की संख्या

लेकिन यह कम से कम -std=gnu++11कमांड लाइन पैरामीटर के बिना जीसीसी में शून्य तर्कों के साथ काम नहीं करता है।

इसलिए मैंने इस समाधान को उसके साथ मिलाने का फैसला किया: 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.