संख्या के तर्क पर मैक्रो को ओवरलोड करना


183

मेरे पास दो मैक्रोज़ FOO2और हैं FOO3:

#define FOO2(x,y) ...
#define FOO3(x,y,z) ...

मैं एक नए मैक्रो FOOको इस प्रकार परिभाषित करना चाहता हूं :

#define FOO(x,y) FOO2(x,y)
#define FOO(x,y,z) FOO3(x,y,z)

लेकिन यह काम नहीं करता है क्योंकि मैक्रोज़ तर्कों की संख्या पर अधिक भार नहीं लेते हैं।

संशोधन के बिना FOO2और FOO3, क्या मैक्रो को परिभाषित करने के लिए FOO( __VA_ARGS__या अन्यथा) को परिभाषित करने का कोई तरीका FOO(x,y)है FOO2, और FOO(x,y,z)करने के लिए प्रेषण के समान प्रभाव पाने के लिए FOO3?


1
मेरे पास बहुत मजबूत भावना है कि इससे पहले कई बार पूछा गया है ... [अपडेट] जैसे यहां
केरेक एसबी

1
@KerrekSB: यह संबंधित हो सकता है, यह सबसे निश्चित रूप से एक नहीं है।
एंड्रयू टॉमाज़ोस

नहीं, शायद ऐसा नहीं है, लेकिन ऐसा कुछ महीने में एक बार आता है ...
केरेक एसबी

C ++ के लिए समान: stackoverflow.com/questions/3046889/… प्रीप्रोसेसरों के मूल रूप से समान होने के बाद से समान होना चाहिए: stackoverflow.com/questions/5085533/…
Ciro Santilli 郝海东 病 六四 stack 法轮功

जवाबों:


262

के रूप में सरल:

#define GET_MACRO(_1,_2,_3,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2)(__VA_ARGS__)

तो अगर आपके पास ये मैक्रोज़ हैं:

FOO(World, !)         # expands to FOO2(World, !)
FOO(foo,bar,baz)      # expands to FOO3(foo,bar,baz)

अगर आपको चौथा चाहिए:

#define GET_MACRO(_1,_2,_3,_4,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO4, FOO3, FOO2)(__VA_ARGS__)

FOO(a,b,c,d)          # expeands to FOO4(a,b,c,d)

स्वाभाविक रूप से, यदि आप परिभाषित करते हैं FOO2, FOO3और FOO4, आउटपुट को परिभाषित मैक्रोज़ द्वारा प्रतिस्थापित किया जाएगा।


5
@ Uroc327 सूची में 0-तर्क मैक्रो जोड़ना संभव है, मेरा उत्तर देखें।
अगुरार

7
Microsoft Visual Studio 2010 पर काम नहीं करता है, VA_ARGS एक एकल मैक्रो तर्क में विस्तारित किया जा रहा है।
.tienne

9
इसे यह जवाब MSVC 2010 के तहत काम करने के लिए मिला ।
28tienne

8
यदि किसी EXPANDको @'stienne के लिंक में उल्लिखित उपयोग करने के तरीके के बारे में उलझन है , तो आप मूल रूप से इसे इस GET_MACROतरह से लागू करते हैं #define FOO(...) EXPAND(GET_MACRO(__VA_ARGS__, FOO3, FOO2, FOO1)(__VA_ARGS__))और इसे msvc में सही संख्या में तर्कों के लिए विस्तारित करना चाहिए।
vexe

3
ध्यान दें कि C ++ 11 पर, आपको एक चेतावनी मिलेगी ISO C++11 requires at least one argument for the "..." in a variadic macro:। इसे ठीक करने के लिए, FOO (...) की परिभाषा में अंतिम पैराम के बाद एक अप्रयुक्त तर्क (या यहां तक ​​कि सिर्फ एक अल्पविराम) जोड़ें: #define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2, UNUSED)(__VA_ARGS__)( इसे कोलिरु पर चलाएँ )।
धातु

49

नेटकोडर के उत्तर को जोड़ने के लिए , आप वास्तव में 0-तर्क मैक्रो के साथ जीसीसी ##__VA_ARGS__एक्सटेंशन की सहायता से कर सकते हैं :

#define GET_MACRO(_0, _1, _2, NAME, ...) NAME
#define FOO(...) GET_MACRO(_0, ##__VA_ARGS__, FOO2, FOO1, FOO0)(__VA_ARGS__)

1
क्या FOO1 और FOO2 की अनुमति देना संभव है लेकिन बिना FOO0 के नहीं #define FOO0 _Pragma("error FOO0 not allowed")?
no --zɐɹƆ

FOO0क्यूटी + mingw32 में काम नहीं कर, कॉल FOO0लागू करेगाFOO1
JustWe

बहुत ही होनहार और सरल। लेकिन FOO0 के साथ -std = c ++ 11 के लिए काम नहीं करता है ... :-(
leonp

1
एक ही समस्या आप सी में यह कर रहे हैं और आप उपयोग करने का प्रयास करता है, तो -std=c99या -std=c11। आपको इसके बजाय -std=gnu99या उपयोग करने की आवश्यकता है-std=gnu11
माइकल Mrozek

1
ऐसा प्रतीत होता है कि इसके _0, ##__VA_ARGS__साथ प्रतिस्थापित करना _0 __VA_OPT__(,) __VA_ARGS__ऐसा करने का नया तरीका है।
Wrzlprmft

36

यहाँ एक अधिक सामान्य समाधान है:

// get number of arguments with __NARG__
#define __NARG__(...)  __NARG_I_(__VA_ARGS__,__RSEQ_N())
#define __NARG_I_(...) __ARG_N(__VA_ARGS__)
#define __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 __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

// general definition for any function name
#define _VFUNC_(name, n) name##n
#define _VFUNC(name, n) _VFUNC_(name, n)
#define VFUNC(func, ...) _VFUNC(func, __NARG__(__VA_ARGS__)) (__VA_ARGS__)

// definition for FOO
#define FOO(...) VFUNC(FOO, __VA_ARGS__)

अपने कार्यों को परिभाषित करें:

#define FOO2(x, y) ((x) + (y))
#define FOO3(x, y, z) ((x) + (y) + (z))

// it also works with C functions:
int FOO4(int a, int b, int c, int d) { return a + b + c + d; }

अब आप FOO2, 3 और 4 तर्कों के साथ उपयोग कर सकते हैं :

FOO(42, 42) // will use makro function FOO2
FOO(42, 42, 42) // will use makro function FOO3
FOO(42, 42, 42, 42) // will call FOO4 function

सीमाएं

  • केवल 63 तर्क तक (लेकिन विस्तार योग्य)
  • केवल जीसीसी में बिना किसी तर्क के कार्य करना

विचार

डिफ़ॉल्ट तर्कों के लिए इसका उपयोग करें:

#define func(...) VFUNC(func, __VA_ARGS__)
#define func2(a, b) func4(a, b, NULL, NULL)
#define func3(a, b, c) func4(a, b, c, NULL)

// real function:
int func4(int a, int b, void* c, void* d) { /* ... */ }

संभावित अनंत तर्कों के साथ कार्यों के लिए इसका उपयोग करें:

#define SUM(...) VFUNC(SUM, __VA_ARGS__)
#define SUM2(a, b) ((a) + (b))
#define SUM3(a, b, c) ((a) + (b) + (c))
#define SUM4(a, b, c) ((a) + (b) + (c) + (d))
// ...

PS: __NARG__यहां लॉरेंट डेनियू एंड रोलैंड इलिग से कॉपी किया गया है: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb-346fc464319b1ee5?pli=1




मैक्रो __NARG_I_पूरी तरह से अनावश्यक और सतही लगता है। यह सिर्फ एक अतिरिक्त कदम और भ्रम जोड़ता है। मैं इसे पूरी तरह हटाने और बस को परिभाषित करने की सलाह देते हैं __NARG__के रूप में के बजाय: #define __NARG__(...) __ARG_N(__VA_ARGS__,__RSEQ_N())
गैब्रियल स्टेपल्स

या कि किसी तरह पूर्व प्रसंस्करण तोड़ देंगे? क्या मैं कुछ भूल रहा हूँ?
गैब्रियल स्टेपल्स

के साथ भी _VFUNC_: बस इसे हटा दें। फिर, के _VFUNCरूप में परिभाषित करें : के #define _VFUNC(name, n) name##nबजाय #define _VFUNC(name, n) _VFUNC_(name, n)
गैब्रियल स्टेपल्स

15

मैं खुद इस पर शोध कर रहा था, और मैं यहाँ इस पर आया था । लेखक ने मैक्रोज़ के माध्यम से सी फ़ंक्शन के लिए डिफ़ॉल्ट तर्क समर्थन जोड़ा।

मैं लेख को संक्षेप में प्रस्तुत करने की कोशिश करूँगा। मूल रूप से, आपको एक मैक्रो को परिभाषित करने की आवश्यकता है जो तर्कों को गिन सकता है। यह मैक्रो 2, 1, 0, या जो भी तर्कों का समर्थन कर सकता है, वापस आ जाएगा। उदाहरण के लिए:

#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)

इसके साथ, आपको एक और मैक्रो बनाने की जरूरत है जो कि तर्कों की एक चर संख्या लेता है, तर्कों को गिनता है, और उपयुक्त मैक्रो को कॉल करता है। मैंने आपका उदाहरण मैक्रो लिया है और इसे लेख के उदाहरण के साथ जोड़ दिया है। मेरे पास FOO1 कॉल फ़ंक्शन है () और FOO2 कॉल फ़ंक्शन एक तर्क b (स्पष्ट रूप से, मैं C ++ यहां मान रहा हूं, लेकिन आप मैक्रो को जो भी बदल सकते हैं)।

#define FOO1(a) a();
#define FOO2(a,b) a(b);

#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)

#define _ONE_OR_TWO_ARGS_1(a) FOO1(a)
#define _ONE_OR_TWO_ARGS_2(a, b) FOO2(a,b)

#define __ONE_OR_TWO_ARGS(N, ...) _ONE_OR_TWO_ARGS_ ## N (__VA_ARGS__)
#define _ONE_OR_TWO_ARGS(N, ...) __ONE_OR_TWO_ARGS(N, __VA_ARGS__)

#define FOO(...) _ONE_OR_TWO_ARGS(NARG2(__VA_ARGS__), __VA_ARGS__)

तो अगर आपके पास है

FOO(a)
FOO(a,b)

प्रीप्रोसेसर इसका विस्तार करता है

a();
a(b);

मैं उस लेख को जरूर पढ़ूंगा जिसे मैंने लिंक किया था। यह बहुत जानकारीपूर्ण है और उन्होंने उल्लेख किया है कि NARG2 खाली तर्क पर काम नहीं करेगा। वह यहां इसका अनुसरण करता है


7

यहां ऊपर दिए गए उत्तर का एक अधिक कॉम्पैक्ट संस्करण है । उदाहरण के साथ।

#include <iostream>
using namespace std;

#define OVERLOADED_MACRO(M, ...) _OVR(M, _COUNT_ARGS(__VA_ARGS__)) (__VA_ARGS__)
#define _OVR(macroName, number_of_args)   _OVR_EXPAND(macroName, number_of_args)
#define _OVR_EXPAND(macroName, number_of_args)    macroName##number_of_args

#define _COUNT_ARGS(...)  _ARG_PATTERN_MATCH(__VA_ARGS__, 9,8,7,6,5,4,3,2,1)
#define _ARG_PATTERN_MATCH(_1,_2,_3,_4,_5,_6,_7,_8,_9, N, ...)   N


//Example:
#define ff(...)     OVERLOADED_MACRO(ff, __VA_ARGS__)
#define ii(...)     OVERLOADED_MACRO(ii, __VA_ARGS__)

#define ff3(c, a, b) for (int c = int(a); c < int(b); ++c)
#define ff2(c, b)   ff3(c, 0, b)

#define ii2(a, b)   ff3(i, a, b)
#define ii1(n)      ii2(0, n)


int main() {
    ff (counter, 3, 5)
        cout << "counter = " << counter << endl;
    ff (abc, 4)
        cout << "abc = " << abc << endl;
    ii (3)
        cout << "i = " << i << endl;
    ii (100, 103)
        cout << "i = " << i << endl;


    return 0;
}

Daud:

User@Table 13:06:16 /c/T
$ g++ test_overloaded_macros.cpp 

User@Table 13:16:26 /c/T
$ ./a.exe
counter = 3
counter = 4
abc = 0
abc = 1
abc = 2
abc = 3
i = 0
i = 1
i = 2
i = 100
i = 101
i = 102

नोट दोनों होने कि _OVRऔर _OVR_EXPANDनिरर्थक लग सकता है, लेकिन यह आवश्यक है पूर्वप्रक्रमक का विस्तार करने के लिए _COUNT_ARGS(__VA_ARGS__)हिस्सा है, जो अन्यथा एक स्ट्रिंग के रूप में व्यवहार किया जाता है।


मुझे यह समाधान पसंद है। क्या शून्य तर्क लेने वाले एक अतिभारित मैक्रो को संभालने के लिए इसे संशोधित किया जा सकता है?
एंड्रयू


2

यहाँ एवगेनी सर्गेव के जवाब से एक स्पिन दूर है। यह एक शून्य तर्क अधिभार के रूप में अच्छी तरह से समर्थन करता है !

मैंने जीसीसी और मिनगडब्ल्यू के साथ इसका परीक्षण किया। इसे C ++ के पुराने और नए संस्करणों के साथ काम करना चाहिए। ध्यान दें कि मैं इसे MSVC के लिए गारंटी नहीं दूंगा ... लेकिन कुछ ट्वीकिंग के साथ, मुझे विश्वास है कि इसे भी काम किया जा सकता है।

मैंने इसे एक हेडर फ़ाइल (जिसे मैंने macroutil.h कहा जाता है) में पेस्ट किया। यदि आप ऐसा करते हैं, तो आप इस शीर्ष लेख को शामिल कर सकते हैं जो भी आपको सुविधा की आवश्यकता है, और कार्यान्वयन में शामिल nastiness को नहीं देखें।

#ifndef MACROUTIL_H
#define MACROUTIL_H

//-----------------------------------------------------------------------------
// OVERLOADED_MACRO
//
// used to create other macros with overloaded argument lists
//
// Example Use:
// #define myMacro(...) OVERLOADED_MACRO( myMacro, __VA_ARGS__ )
// #define myMacro0() someFunc()
// #define myMacro1( arg1 ) someFunc( arg1 )
// #define myMacro2( arg1, arg2 ) someFunc( arg1, arg2 )
//
// myMacro();
// myMacro(1);
// myMacro(1,2);
//
// Note the numerical suffix on the macro names,
// which indicates the number of arguments.
// That is the REQUIRED naming convention for your macros.
//
//-----------------------------------------------------------------------------

// OVERLOADED_MACRO
// derived from: /programming/11761703/overloading-macro-on-number-of-arguments
// replaced use of _COUNT_ARGS macro with VA_NUM_ARGS defined below
// to support of zero argument overloads
#define OVERLOADED_MACRO(M, ...) _OVR(M, VA_NUM_ARGS(__VA_ARGS__)) (__VA_ARGS__)
#define _OVR(macroName, number_of_args)   _OVR_EXPAND(macroName, number_of_args)
#define _OVR_EXPAND(macroName, number_of_args)    macroName##number_of_args
//#define _COUNT_ARGS(...)  _ARG_PATTERN_MATCH(__VA_ARGS__, 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1)
#define _ARG_PATTERN_MATCH(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15, N, ...)   N

// VA_NUM_ARGS
// copied from comments section of:
// http://efesx.com/2010/07/17/variadic-macro-to-count-number-of-arguments/
// which itself was derived from:
// https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
#define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define HAS_NO_COMMA(...) _ARG16(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
#define _TRIGGER_PARENTHESIS_(...) ,

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

#define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define _HAS_ZERO_OR_ONE_ARGS(_0, _1, _2, _3) HAS_NO_COMMA(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define _IS_EMPTY_CASE_0001 ,

#define _VA0(...) HAS_ZERO_OR_ONE_ARGS(__VA_ARGS__)
#define _VA1(...) HAS_ZERO_OR_ONE_ARGS(__VA_ARGS__)
#define _VA2(...) 2
#define _VA3(...) 3
#define _VA4(...) 4
#define _VA5(...) 5
#define _VA6(...) 6
#define _VA7(...) 7
#define _VA8(...) 8
#define _VA9(...) 9
#define _VA10(...) 10
#define _VA11(...) 11
#define _VA12(...) 12
#define _VA13(...) 13
#define _VA14(...) 14
#define _VA15(...) 15
#define _VA16(...) 16

#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, PP_RSEQ_N(__VA_ARGS__) )
#define VA_NUM_ARGS_IMPL(...) VA_NUM_ARGS_N(__VA_ARGS__)

#define VA_NUM_ARGS_N( \
    _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
    _11,_12,_13,_14,_15,_16,N,...) N

#define PP_RSEQ_N(...) \
    _VA16(__VA_ARGS__),_VA15(__VA_ARGS__),_VA14(__VA_ARGS__),_VA13(__VA_ARGS__), \
    _VA12(__VA_ARGS__),_VA11(__VA_ARGS__),_VA10(__VA_ARGS__), _VA9(__VA_ARGS__), \
    _VA8(__VA_ARGS__),_VA7(__VA_ARGS__),_VA6(__VA_ARGS__),_VA5(__VA_ARGS__), \
    _VA4(__VA_ARGS__),_VA3(__VA_ARGS__),_VA2(__VA_ARGS__),_VA1(__VA_ARGS__), \
    _VA0(__VA_ARGS__)

//-----------------------------------------------------------------------------

#endif // MACROUTIL_H

2

यह GCC, Clang और MSVC पर ठीक काम करता है। यह यहां के कुछ उत्तरों का साफ किया हुआ संस्करण है

#define _my_BUGFX(x) x

#define _my_NARG2(...) _my_BUGFX(_my_NARG1(__VA_ARGS__,_my_RSEQN()))
#define _my_NARG1(...) _my_BUGFX(_my_ARGSN(__VA_ARGS__))
#define _my_ARGSN(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N,...) N
#define _my_RSEQN() 10,9,8,7,6,5,4,3,2,1,0

#define _my_FUNC2(name,n) name ## n
#define _my_FUNC1(name,n) _my_FUNC2(name,n)
#define GET_MACRO(func,...) _my_FUNC1(func,_my_BUGFX(_my_NARG2(__VA_ARGS__))) (__VA_ARGS__)

#define FOO(...) GET_MACRO(FOO,__VA_ARGS__)

1
@RianQuinn इस मैक्रो को कैसे समायोजित करें ताकि यह शून्य तर्क के साथ काम करे #define func0() foo? वर्तमान संस्करण दुर्भाग्य से इस मामले को नहीं संभालता है।
जेरी मा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.