programing

매크로가 인수 수에 따라 오버로드될 수 있습니까?

muds 2023. 6. 10. 09:49
반응형

매크로가 인수 수에 따라 오버로드될 수 있습니까?

이것은 어떻게 작동합니까?C99/C++11 변수 매크로가 얼마나 많은 인수를 제공하는지에 따라 다른 것으로 확장되도록 어떻게 구현될 수 있습니까?

(편집: 이미 만들어진 솔루션은 끝을 참조하십시오.)

오버로드된 매크로를 얻으려면 먼저 여러 구현 중에서 선택하는 매크로가 필요합니다.이 부품은 변수 매크로를 사용하지 않습니다.그런 다음 일반적으로 인수를 카운트하는 변수 매크로가 선택기를 생성합니다.인수 개수를 디스패처에 연결하면 오버로드된 매크로가 생성됩니다.

주의: 이 시스템은 인수가 없거나 빈 인수가 하나이므로 0과 하나의 인수를 구분할 수 없습니다.그들은 둘다 처럼 보입니다.MACRO().


구현을 선택하려면 함수와 같은 매크로가 있는 매크로 분류 연산자를 사용합니다.

#define select( selector, ... ) impl ## _ ## selector( __VA_ARGS__ )
#define impl_1() meh
#define impl_2( abc, xyz ) # abc "wizza" xyz()
//etc

// usage: select( 1 ) => impl_1() => meh
//        select( 2, huz, bar ) => impl_2( huzza, bar ) => "huz" "wizza" bar()

왜냐하면.##연산자는 인수의 매크로 확장을 억제하므로 다른 매크로로 묶는 것이 좋습니다.

#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )

인수를 계산하려면 사용__VA_ARGS__다음과 같이 인수를 이동합니다(이것은 영리한 부분입니다).

#define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 )

라이브러리 코드:

#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )

#define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 )

#define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)

용도:

#define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ )
#define MY_OVERLOADED_1( X ) foo< X >
#define MY_OVERLOADED_2( X, Y ) bar< X >( Y )
#define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z()

저는 이것을 Potatoswater의 게시물에 댓글로 게시하고 싶지만, 너무 길고 코드 목록이 필요합니다.

다음은 오버로드된 매크로를 의미하는 매크로 집합을 생성하기 위한 펄 코드의 일부입니다.

$ perl -le 'map{
        $arity = $_; map {
                $ar = 2 + $arity + $_; $arm = $ar - 1; $arlist = join("", map{"A$_, "} 1..$arity); $warlist = "WHAT, $arlist";
                @li = map {"_$_"} 0..$_; $lis = join(", ", @li); $lim = pop @li; $lims = join(", ", @li);
                print "#define FEI_${arity}A_$ar($warlist$lis) FEI_${arity}A_$arm($warlist$lims) WHAT($_, $arlist$lim)"
        } 1..3; print ""
} 0..4'

다음은 스크립트의 출력입니다.

#define FEI_0A_3(WHAT, _0, _1) FEI_0A_2(WHAT, _0) WHAT(1, _1)
#define FEI_0A_4(WHAT, _0, _1, _2) FEI_0A_3(WHAT, _0, _1) WHAT(2, _2)
#define FEI_0A_5(WHAT, _0, _1, _2, _3) FEI_0A_4(WHAT, _0, _1, _2) WHAT(3, _3)

#define FEI_1A_4(WHAT, A1, _0, _1) FEI_1A_3(WHAT, A1, _0) WHAT(1, A1, _1)
#define FEI_1A_5(WHAT, A1, _0, _1, _2) FEI_1A_4(WHAT, A1, _0, _1) WHAT(2, A1, _2)
#define FEI_1A_6(WHAT, A1, _0, _1, _2, _3) FEI_1A_5(WHAT, A1, _0, _1, _2) WHAT(3, A1, _3)

#define FEI_2A_5(WHAT, A1, A2, _0, _1) FEI_2A_4(WHAT, A1, A2, _0) WHAT(1, A1, A2, _1)
#define FEI_2A_6(WHAT, A1, A2, _0, _1, _2) FEI_2A_5(WHAT, A1, A2, _0, _1) WHAT(2, A1, A2, _2)
#define FEI_2A_7(WHAT, A1, A2, _0, _1, _2, _3) FEI_2A_6(WHAT, A1, A2, _0, _1, _2) WHAT(3, A1, A2, _3)

#define FEI_3A_6(WHAT, A1, A2, A3, _0, _1) FEI_3A_5(WHAT, A1, A2, A3, _0) WHAT(1, A1, A2, A3, _1)
#define FEI_3A_7(WHAT, A1, A2, A3, _0, _1, _2) FEI_3A_6(WHAT, A1, A2, A3, _0, _1) WHAT(2, A1, A2, A3, _2)
#define FEI_3A_8(WHAT, A1, A2, A3, _0, _1, _2, _3) FEI_3A_7(WHAT, A1, A2, A3, _0, _1, _2) WHAT(3, A1, A2, A3, _3)

#define FEI_4A_7(WHAT, A1, A2, A3, A4, _0, _1) FEI_4A_6(WHAT, A1, A2, A3, A4, _0) WHAT(1, A1, A2, A3, A4, _1)
#define FEI_4A_8(WHAT, A1, A2, A3, A4, _0, _1, _2) FEI_4A_7(WHAT, A1, A2, A3, A4, _0, _1) WHAT(2, A1, A2, A3, A4, _2)
#define FEI_4A_9(WHAT, A1, A2, A3, A4, _0, _1, _2, _3) FEI_4A_8(WHAT, A1, A2, A3, A4, _0, _1, _2) WHAT(3, A1, A2, A3, A4, _3)

생성에 사용되는 매크로 오버로드 그룹(정기적으로 구조화된 섹션)입니다.FOR_EACH(일명:FE) 를 디스패치할 수 있는WHAT매크로 선택적으로 임의의 수의 상수 인수(A1,A2...) 목록에서 임의의 수의 인수 외에 적절한 순서의 인덱스(같은 것을 사용하지 않는 순진한 구현)도 있습니다.SELECT오버로딩의 경우 역지수가 산출됩니다.

예를 들어, 나머지 섹션(두 번째 블록의 비정규 "베이스 케이스" 부분)은 다음과 같습니다.

#define FE_INDEXED_1ARG(...) VA_SELECT(FEI_1A, __VA_ARGS__)
#define FEI_1A_3(WHAT, A1, _0) WHAT(0, A1, _0)

이것의 유용성은 아마도 문제가 될 수 있습니다(나는 그것의 용도를 보았기 때문에 그것을 만들었습니다...). 그리고 이것도 OP의 질문에 직접적으로 대답하지 않습니다(사실, 그것은 반대입니다--각 구성에 대해 모든 변수 인수에 동일한 작업을 수행합니다...).하지만 저는 그 기술이 꽤 흥미롭고 (어떤 면에서는 완전히 소름끼치고) 프리프로세서를 사용하는 표현력을 허용하고 이런 방식으로 매우 효율적인 기계 코드를 생성하는 것이 가능할 것이라고 생각했습니다.개인적으로 C 전처리기가 아직 개선의 여지가 있다고 생각하는 이유를 보여주는 가슴 아픈 사례로도 작용한다고 생각합니다.

즉, C 전처리기는 절대적으로 혐오스럽고 우리는 아마도 그것을 폐기하고 처음부터 시작해야 한다는 것입니다 :)

다음은 0과 1의 주장을 구별할 수 있는 포타토스스와터의 답변을 개선한 것입니다.

간단히 말해서, 언제__VA_ARGS__비어 있습니다.EXPAND __VA_ARGS__ ()東京의 VA_SIZE가 매로가됩니다가 됩니다.EXPAND ()6개의 쉼표로 대체됩니다.그렇게,VA_SIZE... 되다COMPOSE( GET_COUNT, (,,,,,, , 0, 6, 5, 4, 3, 2, 1) ) 그것은 그것고은그리가 됩니다.GET_COUNT (,,,,,, , 0, 6, 5, 4, 3, 2, 1)0.0µs를 합니다.

반에면 때는 제언.__VA_ARGS__예를 들어,int, 5,EXPAND __VA_ARGS__ () 되다EXPAND int, 5 ().그렇게,VA_SIZE... 되다COMPOSE( GET_COUNT, (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) ) 되는GET_COUNT (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1)그리고 포타토스스와터의 대답에 설명된 대로 2를 반환합니다.

알겠습니다.EXPAND제이슨 당의 대답에서 나온 아이디어.

라이브러리 코드:

#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )
#define COMPOSE( NAME, ARGS ) NAME ARGS

#define GET_COUNT( _0, _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define EXPAND() ,,,,,, // 6 commas (or 7 empty tokens)
#define VA_SIZE( ... ) COMPOSE( GET_COUNT, (EXPAND __VA_ARGS__ (), 0, 6, 5, 4, 3, 2, 1) )

#define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)

용도:

#define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ )

#define MY_OVERLOADED_0( ) meh()
#define MY_OVERLOADED_1( X ) foo< X >
#define MY_OVERLOADED_2( X, Y ) bar< X >( Y )
#define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z()

MY_OVERLOADED()                // meh()
MY_OVERLOADED(bool)            // foo< bool >
MY_OVERLOADED(int, 5)          // bar< int >( 5 )
MY_OVERLOADED(me, double, now) // bang_me< double >.now()

이미 답변이 끝났지만, 저는 아주 짧은 버전을 준비했습니다.도움이 되길 바랍니다.

실행

// Variable Argument Macro (VA_MACRO) upto 6 arguments
#define NUM_ARGS_(_1, _2, _3, _4, _5, _6, TOTAL, ...) TOTAL
#define NUM_ARGS(...) NUM_ARGS_(__VA_ARGS__, 6, 5, 4, 3, 2, 1)

#define CONCATE_(X, Y) X##Y  // Fixed the double '_' from previous code
#define CONCATE(MACRO, NUMBER) CONCATE_(MACRO, NUMBER)
#define VA_MACRO(MACRO, ...) CONCATE(MACRO, NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)

사용자 정의

// This is how user may define own set of variadic macros
#define MY_MACRO(...) VA_MACRO(MY_MACRO, __VA_ARGS__)
#define MY_MACRO1(_1) "One"
#define MY_MACRO2(_1, _2) "Two"
#define MY_MACRO3(_1, _2, _3) "Three"

사용.

// While using those, user needs to use only the main macro
int main ()
{
  auto one = MY_MACRO(1);
  auto two = MY_MACRO(1, 2); 
  auto three = MY_MACRO(1, 2, 3); 
}

저는 물을 피하기 위해 감자에서 물로 용액을 확장했습니다.iso c99 requires rest arguments to be used가 gcc를 전환할 때 가 합니다.-pedantic사용 중입니다.
합니다. 0 매 변 수 있 수 니 습 다 할

도서관

#define NUM_ARGS_(_1, _2, _3, _4, _5, _6, _7, _8, TOTAL, ...) TOTAL
#define NUM_ARGS(...) NUM_ARGS_(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define CONCATE_(X, Y) X##Y
#define CONCATE(MACRO, NUMBER) CONCATE_(MACRO, NUMBER)
#define VA_MACRO(MACRO, ...) CONCATE(MACRO, NUM_ARGS (__VA_ARGS__))(__VA_ARGS__)

사용자 정의

이에서는 다음과 같습니다.MacroTest오버로드된 C++ 함수입니다.그러나 여기에 함수를 배치할 수 있습니다.

#define MY_OVERLOADED(...) VA_MACRO(MY_OVERLOADED, void, void, __VA_ARGS__)
#define MY_OVERLOADED0(s, t) MacroTest()
#define MY_OVERLOADED1(s, t, a) MacroTest( a)
#define MY_OVERLOADED2(s, t, a, b) MacroTest(a, b)
#define MY_OVERLOADED3(s, t, a, b, c) MacroTest(a, b, c)

사용.

MY_OVERLOADED();
MY_OVERLOADED(1);
MY_OVERLOADED(11, 22);
MY_OVERLOADED(111, 222, 333);

전체 예제

https://www.onlinegdb.com/online_c++_compiler https://www.jdoodle.com/online-compiler-c++/ 에서 테스트됨

#include <stdio.h>

#define NUM_ARGS_(_1, _2, _3, _4, _5, _6, _7, _8, TOTAL, ...) TOTAL
#define NUM_ARGS(...) NUM_ARGS_(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define CONCATE_(X, Y) X##Y
#define CONCATE(MACRO, NUMBER) CONCATE_(MACRO, NUMBER)
#define VA_MACRO(MACRO, ...) CONCATE(MACRO, NUM_ARGS (__VA_ARGS__))(__VA_ARGS__)

void MacroTest() { printf("no param\n"); }
void MacroTest(int p1) { printf("p1=%i\n", p1); }
void MacroTest(int p1, int p2) { printf("p1=%i p2=%i\n", p1, p2); }
void MacroTest(int p1, int p2, int p3) { printf("p1=%i p2=%i p3=%i\n", p1, p2, p3); }

#define MY_OVERLOADED(...) VA_MACRO(MY_OVERLOADED, void, void, __VA_ARGS__)
#define MY_OVERLOADED0(s, t) MacroTest()
#define MY_OVERLOADED1(s, t, a) MacroTest( a)
#define MY_OVERLOADED2(s, t, a, b) MacroTest(a, b)
#define MY_OVERLOADED3(s, t, a, b, c) MacroTest(a, b, c)

int main()
{
    printf("gcc %i.%i.%i\n", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
    printf("MY_OVERLOADED()             : ");
    MY_OVERLOADED();
    printf("MY_OVERLOADED(1)            : ");
    MY_OVERLOADED(1);
    printf("MY_OVERLOADED(11, 22)       : ");
    MY_OVERLOADED(11, 22);
    printf("MY_OVERLOADED(111, 222, 333): ");
    MY_OVERLOADED(111, 222, 333);

    return 0;
}

언급URL : https://stackoverflow.com/questions/16683146/can-macros-be-overloaded-by-number-of-arguments

반응형