인수 수에 대한 매크로 오버로드


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)

그러나 매크로는 여러 인수에 과부하가 걸리지 않기 때문에 작동하지 않습니다.

수정하지 않고 FOO2FOO3매크로 정의하는 몇 가지 방법이있다 FOO(사용 __VA_ARGS__파견 같은 효과를 얻을 수있는 다른 나) FOO(x,y)FOO2, 그리고 FOO(x,y,z)하려면 FOO3?


1
나는 이것이 몇 번 전에 요청되었다는 매우 강한 느낌을 가지고있다 ... [update] eg here .
Kerrek SB

1
@ KerrekSB : 관련이있을 수 있습니다.
Andrew Tomazos

아니, 아마 그런 것은 아니지만, 이런 일이 한 달에 한 번 정도 나타납니다 ...
Kerrek SB

C ++의 경우 : stackoverflow.com/questions/3046889/… 전처리 기가 기본적으로 동일하므로 동일해야 함 : stackoverflow.com/questions/5085533/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

답변:


264

다음과 같이 간단합니다.

#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 인수 매크로를 목록에 추가하는 것이 가능합니다. 내 답변을 참조하십시오.
augurar

7
Microsoft Visual Studio 2010에서 작동하지 않으면 VA_ARGS 가 단일 매크로 인수로 확장 된 것 같습니다.
Étienne

9
발견 이 대답 은 MSVC 2010에서 작동하도록
에티엔

8
EXPAND@ Étienne의 링크에서 언급 한 사용 방법에 대해 혼란 스럽다면 기본적으로 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__) ( Coliru에서 실행 참조 ).
금속

49

netcoder의 답변 에 추가하기 위해 실제로 GCC ##__VA_ARGS__확장을 사용하여 0 인수 매크로를 사용 하여이 작업을 수행 할 수 있습니다.

#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ɐɹƆ

FOO0qt + mingw32에서 작동하지 않으면 FOO0호출은FOO1
JustWe

매우 유망하고 단순합니다. 그러나 -std = c ++ 11 ... :-(
leonp

1
C 에서이 작업을 수행 -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 개의 인수 만 가능하지만 확장 가능
  • GCC에서만 인수가없는 기능 가능

아이디어

기본 인수로 사용하십시오.

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

추신 : __NARG__Laurent Deniau & Roland Illig에서 복사했습니다 : 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()).
Gabriel Staples

아니면 어떻게 든 전처리가 중단됩니까? 뭔가 빠졌습니까?
Gabriel Staples

동일 _VFUNC_: 그냥 삭제하십시오. 그런 다음, 정의 _VFUNC로 : #define _VFUNC(name, n) name##n대신 #define _VFUNC(name, n) _VFUNC_(name, n).
Gabriel Staples

15

나는 이것을 스스로 연구하고 있었고 여기 에서 이것을 보았습니다 . 저자는 매크로를 통해 C 함수에 대한 기본 인수 지원을 추가했습니다.

나는 기사를 간단히 요약하려고 노력할 것이다. 기본적으로 인수를 계산할 수있는 매크로를 정의해야합니다. 이 매크로는 2, 1, 0 또는 지원 가능한 모든 범위의 인수를 반환합니다. 예 :

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

이를 통해 가변 개수의 인수를 취하고 인수를 세고 적절한 매크로를 호출하는 다른 매크로를 작성해야합니다. 예제 매크로를 가져 와서 기사 예제와 결합했습니다. FOO1 호출 함수 a () 및 FOO2 호출 함수 a와 인수 b가 있습니다.

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

운영:

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__)부품 을 확장해야하며 , 그렇지 않으면 문자열로 처리됩니다.


나는이 해결책을 좋아한다. 인수가 0 인 오버로드 된 매크로를 처리하도록 수정할 수 있습니까?
앤드류


2

Evgeni Sergeev의 답변에서 나온 내용입니다. 이것도 제로 인수 과부하 를 지원 합니다 !

나는 이것을 GCC와 MinGW로 테스트했다. 이전 버전과 새로운 버전의 C ++에서 작동해야합니다. MSVC에 대해서는 보장하지 않을 것입니다 ...하지만 약간의 조정으로도 작동 할 수 있다고 확신합니다.

또한 헤더 파일 (매크로 유틸리티 .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이 매크로를 조정하여 인수가 0이되도록하는 방법은 #define func0() foo무엇입니까? 현재 버전은 불행히도이 경우를 처리하지 않습니다.
Jerry Ma
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.