IT

C ++ 매크로를 사용하는 선행 변수

lottoking 2020. 8. 24. 20:39
반응형

C ++ 매크로를 사용하는 선행 변수


C ++ 매크로로 구독 변수를 얻는 방법이 있습니까? 어떤 종류의 보강도 좋을 것입니다.


여기에 한 가지 방법이 있습니다. 인수 목록을 두 번 사용하여 먼저 도우미 매크로의 이름을 만든 다음 해당 도우미 매크로에 인수를 전달합니다. 표준 트릭을 사용하여 매크로에 대한 인수 수를 계산합니다.

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};

void PrintString(const char* message, int size, int style)
{
}

#define PRINT_STRING_1_ARGS(message)              PrintString(message, 0, 0)
#define PRINT_STRING_2_ARGS(message, size)        PrintString(message, size, 0)
#define PRINT_STRING_3_ARGS(message, size, style) PrintString(message, size, style)

#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define PRINT_STRING_MACRO_CHOOSER(...) \
    GET_4TH_ARG(__VA_ARGS__, PRINT_STRING_3_ARGS, \
                PRINT_STRING_2_ARGS, PRINT_STRING_1_ARGS, )

#define PRINT_STRING(...) PRINT_STRING_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main(int argc, char * const argv[])
{
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

이렇게하면 작성자가 아닌 매크로 호출자가 더 쉽게 수행 할 수 있습니다.


그의 답변에 대해 Derek Ledbetter에게 큰 존경을 표하며 오래된 질문을 되살린 것에 대해드립니다.

일을하고이보다 선행 할 수있는 능력에 다른 따기 있었는지의 이해를 얻기 __VA_ARGS__##허용 나를 [해석] 변화를 가지고 올 ...

// The multiple macros that you would need anyway [as per: Crazy Eddie]
#define XXX_0()                     <code for no arguments> 
#define XXX_1(A)                    <code for one argument> 
#define XXX_2(A,B)                  <code for two arguments> 
#define XXX_3(A,B,C)                <code for three arguments> 
#define XXX_4(A,B,C,D)              <code for four arguments>  

// The interim macro that simply strips the excess and ends up with the required macro
#define XXX_X(x,A,B,C,D,FUNC, ...)  FUNC  

// The macro that the programmer uses 
#define XXX(...)                    XXX_X(,##__VA_ARGS__,\
                                          XXX_4(__VA_ARGS__),\
                                          XXX_3(__VA_ARGS__),\
                                          XXX_2(__VA_ARGS__),\
                                          XXX_1(__VA_ARGS__),\
                                          XXX_0(__VA_ARGS__)\
                                         ) 

답을 우연히 발견했지만 어떻게 작동하는지 잘 모르는 저와 같은 비전문가를 위해 다음 코드로 시작하여 실제 처리를 단계별로 진행하겠습니다.

XXX();
XXX(1); 
XXX(1,2); 
XXX(1,2,3); 
XXX(1,2,3,4); 
XXX(1,2,3,4,5);      // Not actually valid, but included to show the process 

된다 ...

XXX_X(, XXX_4(), XXX_3(),  XXX_2(),    XXX_1(),      XXX_0()         );
XXX_X(, 1,       XXX_4(1), XXX_3(1),   XXX_2(1),     XXX_1(1),       XXX_0(1)          );
XXX_X(, 1,       2,        XXX_4(1,2), XXX_3(1,2),   XXX_2(1,2),     XXX_1(1,2),       XXX_0(1,2)        );
XXX_X(, 1,       2,        3,          XXX_4(1,2,3), XXX_3(1,2,3),   XXX_2(1,2,3),     XXX_1(1,2,3),     XXX_0(1,2,3)      );
XXX_X(, 1,       2,        3,          4,            XXX_4(1,2,3,4), XXX_3(1,2,3,4),   XXX_2(1,2,3,4),   XXX_1(1,2,3,4),   XXX_0(1,2,3,4)    );
XXX_X(, 1,       2,        3,          4,            5,              XXX_4(1,2,3,4,5), XXX_3(1,2,3,4,5), XXX_2(1,2,3,4,5), XXX_1(1,2,3,4,5), XXX_0(1,2,3,4,5) );

이것이 여섯 번째 주장입니다.

XXX_0(); 
XXX_1(1); 
XXX_2(1,2); 
XXX_3(1,2,3); 
XXX_4(1,2,3,4); 
5; 

추신 : 구매 오류가 발생했을 경우 XXX_0에 대한 #define을 제거하십시오 [예 : 인수가없는 경우 제거하지 않는 경우].

PPS : 잘못된 상황 (예 : 5)이 프로그래머에게 더 명확한 오류를 제공하는 것이 좋을 것입니다!

PPPS : 저는가 아니기 때문에 의견 (좋음, 나쁨 또는 기타)을 듣게되어 매우 기쁩니다!


C ++ 매크로는 C에서 변경되지 않습니다. C에는 함수에 대한 오버로딩 및 기본 인수가 확실히 매크로에 대한 인수가 없습니다. 귀하의 질문에 대답하십시오. 아니요, 기능은 매크로에 존재하지 않습니다. 유일한 옵션은 이름이 다른 여러 매크로를 정의하는 것입니다.

참고로 : C ++에서는 일반적으로 가능한 한 매크로에서 벗어나는 것이 좋습니다. 이와 같은 기능이 필요한 경우 매크로를 과도하게 사용하고있을 가능성이 있습니다.


에 가장 큰 관련하여 데렉 레드베터 , 데이비드 Sorkovsky , Syphorlate 함께 의해 빈 매크로 인자를 감지하는 독창적 인 방법으로 자신의 답변을, 옌스 Gustedt 에서

https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

마침내 나는 모든 트릭을 통합하는 논의를 내놓았습니다. 그래서 해결책은

  1. 함수 오버로딩을 달성하기 위해 표준 C99 매크로 만 사용하고 GCC / CLANG / MSVC 확장은 포함하지 않습니다 (즉, , ##__VA_ARGS__GCC / CLANG 에 대한 특정 의해 쉼표 삼키기 ##__VA_ARGS__MSVC 대한 암시 적 삼키기 ). 따라서 --std=c99원하는 경우 컴파일러에 누락 된 부분 자유롭게 전달하십시오 =)
  2. 에 맞게 추가 필요로 확장하는 경우 인수가 무제한 일뿐 만 아니라 인수무제한 인 경우 에도 작동 합니다.
  3. 합리적으로 크로스 플랫폼 에서 작동 하고 최소한의 테스트

    • GNU / Linux + GCC (CentOS 7.0 x86_64의 GCC 4.9.2)
    • GNU / Linux + CLANG / LLVM , (CentOS 7.0 x86_64의 CLANG / LLVM 3.5.0)
    • OS X + Xcode , (OS X Yosemite 10.10.1의 XCode 6.1.1)
    • Windows + Visual Studio , (Windows 7 SP1 64 비트의 Visual Studio 2013 업데이트 4)

lazies의 경우 소스를 복사 한 게시물의 맨 마지막으로 건너 뛰십시오. 아래는 __VA_ARGS__저와 같은 일반적인 솔루션을 찾는 모든 사람들을 돕고 영감을주는 자세한 설명 입니다. =)

방법은 다음과 가변합니다. 우선 그것을 명명은 사용자에게 표시 오버 "기능"정의을 create하고 관련 실제 함수 정의 realCreate및 다른 인자 수와 매크로 정의는 CREATE_2, CREATE_1, CREATE_0, 아래와 같이

#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

MACRO_CHOOSER(__VA_ARGS__)부분은 매크로 정의 이름을 해결하고, 둘째 (__VA_ARGS__)부분은 매크로를 포함한다. 에 대한 사용자의 호출 그래서 create(10)로 확인 CREATE_1(10)CREATE_1부분에서 유래 MACRO_CHOOSER(__VA_ARGS__)하고, 일부 (10)는 두 번째에서 온다 (__VA_ARGS__).

는 경우 MACRO_CHOOSER, 그 트릭을 사용 비어, 다음 사용 __VA_ARGS__가능한 매크로 호출로 연결됩니다.

NO_ARG_EXPANDER __VA_ARGS__ ()  // simply shrinks to NO_ARG_EXPANDER()

독창적 으로이 매크로 호출 결과를 다음과 같이 정의 할 수 있습니다.

#define NO_ARG_EXPANDER() ,,CREATE_0

두 개의 쉼표는 곧 설명됩니다. 다음 유용한 매크로는

#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())

그래서의 부름

create();
create(10);
create(20, 20);

실제로 확장됩니다

CHOOSE_FROM_ARG_COUNT(,,CREATE_0)();
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 10 ())(10);
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 20, 20 ())(20, 20);

매크로 이름에서 알 수 있더라도 나중에 인수 수를 계산해야합니다. 여기에 또 다른 트릭이 있습니다. 전처리 기는 단순한 텍스트 교체 만 수행합니다. 괄호 안에있는 쉼표의 수만으로 매크로 호출의 인수 수를 추론합니다. 쉼표로 구분 된 실제 "인수"는 유효한 구문 일 필요는 없습니다. 모든 텍스트가 될 수 있습니다. 즉, 위의 예 NO_ARG_EXPANDER 10 ()에서 중간 호출에 대해 1 개의 인수로 계산됩니다. NO_ARG_EXPANDER 2020 ()바닥 바닥 호출 2 개 인자로 계산됩니다.

다음 도우미 매크로를 사용하여 추가 확장하면

##define CHOOSE_FROM_ARG_COUNT(...) \
  FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
  FUNC_CHOOSER argsWithParentheses

후행은 ,CREATE_1작업 주위 GCC / 꽝에 대한 것을 말하는 (위양성) 오류 ISO C99 requires rest arguments to be used무시 이다 통과 할 때 -pedantic컴파일러에 있습니다. FUNC_RECOMPOSERMSVC에 대한 해결 방법, 또는 매크로 호출의 괄호 안의 인수의 수 (즉, 쉼표)를 셀 수 없다. 결과는 다음과 같이 추가로 해결됩니다.

FUNC_CHOOSER (,,CREATE_0, CREATE_2, CREATE_1, )();
FUNC_CHOOSER (NO_ARG_EXPANDER 10 (), CREATE_2, CREATE_1, )(10);
FUNC_CHOOSER (NO_ARG_EXPANDER 20, 20 (), CREATE_2, CREATE_1, )(20, 20);

독수리 눈을 가진 사람처럼 우리가 필요로하는 마지막 단계는 표준 인수 계수 트릭을 사용하여 원하는 매크로 이름을 최종적으로 선택하는 것입니다.

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3

결과를

CREATE_0();
CREATE_1(10);
CREATE_2(20, 20);

그리고 확실히 원하는 실제 함수 호출을 제공합니다.

realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);

더 나은 가독성을 위해 명령문을 재정렬하여 모두 합치면 2- 인수 예제전체 소스 는 다음과 가변합니다.

#include <stdio.h>

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define NO_ARG_EXPANDER() ,,CREATE_0
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main()
{
  create();
  create(10);
  create(20, 20);
  //create(30, 30, 30);  // Compilation error
  return 0;
}

복잡하고 추악하며 API 개발자에게 부담이되는 C / C ++ 함수의 언급 변수를 오버로딩하고 미친 사람들에게 설정하는 솔루션이 있습니다. 앞으로 나오는 오버로드 된 API의 사용은 매우 즐겁고 즐거워입니다. =)

이 접근 방식을 더 단순화 할 수있는 경우 다음 주소로 알려주십시오.

https://github.com/jason-deng/C99FunctionOverload

이 작품을 발표하도록 영감을주고 준 모든 훌륭한 사람들에게 다시 한 번 감사드립니다! =)


Visual C ++에서 작동하는 일부 VA_NARGS 솔루션을 고통스럽게 검색하는 사람을 위해. 다음 매크로는 Visual C ++ Express 2010에서 완벽하게 작동했습니다 (매개 변수도 0입니다!).

#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,N,...) N
#define VA_NUM_ARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple
#define VA_NARGS(...)  bool(#__VA_ARGS__) ? (VA_NUM_ARGS_IMPL_((__VA_ARGS__, 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

요청한 경우 다음을 수행 할 수 있습니다.

//macro selection(vc++)
#define SELMACRO_IMPL(_1,_2,_3, N,...) N
#define SELMACRO_IMPL_(tuple) SELMACRO_IMPL tuple
#define mymacro1(var1) var1
#define mymacro2(var1,var2) var2*var1
#define mymacro3(var1,var2,var3) var1*var2*var3
#define mymacro(...) SELMACRO_IMPL_((__VA_ARGS__, mymacro3(__VA_ARGS__), mymacro2(__VA_ARGS__), mymacro1(__VA_ARGS__))) 

그것은 vc라고 저에게 맞는군요. 그러나 매개 변수가없는 경우에는 작동하지 않습니다.

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8

gcc/ varargs 매크로를g++ 지원 하지만 그것이 표준이라고 생각하지 않습니다.


#include <stdio.h>

#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

#define PP_CONCAT(a,b) PP_CONCAT_(a,b)
#define PP_CONCAT_(a,b) a ## b

#define THINK(...) PP_CONCAT(THINK_, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#define THINK_0() THINK_1("sector zz9 plural z alpha")
#define THINK_1(location) THINK_2(location, 42)
#define THINK_2(location,answer) THINK_3(location, answer, "deep thought")
#define THINK_3(location,answer,computer) \
  printf ("The answer is %d. This was calculated by %s, and a computer to figure out what this"
          " actually means will be build in %s\n", (answer), (computer), (location))

int
main (int argc, char *argv[])
{
  THINK (); /* On compilers other than GCC you have to call with least one non-default argument */
}

면책 조항 : 대부분 무해합니다.


그것은 실제로 전처리 기가 것이 아닙니다.

즉, 어느 정도의 가독성으로 심각하게 까다로운 매크로 프로그래밍 영역에 좀 더 부스트 전 처리기 라이브러리를 살펴보아야 합니다 . 결국 튜링과 완전히 호환되는 프로그래밍 수준 (전 처리기, 템플릿 메타 프로그래밍 및 기본 수준 C ++)이 세 가지 모두 C ++가 아닐 것입니다!


#define MY_MACRO_3(X,Y,Z) ...
#define MY_MACRO_2(X,Y) MY_MACRO(X,Y,5)
#define MY_MACRO_1(X) MY_MACRO(X,42,5)

호출 시점에서 얼마나 많은 인수를 거기에서 실제로 오버로딩 할 필요가 없습니다.


Derek Ledbetter 코드의 더 간결한 버전 :

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};


void PrintString(const char* message = NULL, int size = 0, int style = 0)
{
}


#define PRINT_STRING(...) PrintString(__VA_ARGS__)


int main(int argc, char * const argv[])
{ 
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

도서관 BOOST_PP_OVERLOAD에서 사용할 수 있습니다 boost.

공식 부스트 문서의:

#include <boost/preprocessor/facilities/overload.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>

#define MACRO_1(number) MACRO_2(number,10)
#define MACRO_2(number1,number2) BOOST_PP_ADD(number1,number2)

#if !BOOST_PP_VARIADICS_MSVC

#define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__)

#else

// or for Visual C++

#define MACRO_ADD_NUMBERS(...) \
  BOOST_PP_CAT(BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())

#endif

MACRO_ADD_NUMBERS(5) // output is 15
MACRO_ADD_NUMBERS(3,6) // output is 9

필요한 것에 따라 매크로를 사용 하여 var args수행 할 수 있습니다. 이제 선택적 매개 변수 나 매크로 오버로딩은 없습니다.


끔찍한 매크로 몬스터의 열렬한 팬으로서 Jason Deng의 답변을 확장하고 실제로 사용할 수 있도록 만들고 싶었습니다. (더 좋든 나쁘 든간에.) 원본은 새 매크로를 만들 때마다 큰 알파벳 수프를 수정해야하기 때문에 사용하기에 그리 좋지 않으며 다른 양의 인수가 필요한 경우 더 나쁩니다.

그래서 다음과 같은 기능을 갖춘 버전을 만들었습니다.

  • 0 인수 케이스 작동
  • 지저분한 부분을 수정하지 않고 1 ~ 16 개의 인수
  • 더 많은 매크로 함수를 작성하기 쉽습니다.
  • gcc 10, clang 9, Visual Studio 2017에서 테스트되었습니다.

현재는 최대 16 개의 인수를 설정했지만 더 필요한 경우 (정말 지금? 그냥 어리석은 것 같습니다 ...) FUNC_CHOOSER 및 CHOOSE_FROM_ARG_COUNT를 편집 한 다음 NO_ARG_EXPANDER에 쉼표를 추가 할 수 있습니다.

구현에 대한 자세한 내용은 Jason Deng의 훌륭한 답변을 참조하십시오.하지만 여기에 코드를 넣겠습니다.

#include <stdio.h>

void realCreate(int x, int y)
{
    printf("(%d, %d)\n", x, y);
}

// This part you put in some library header:
#define FUNC_CHOOSER(_f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10, _f11, _f12, _f13, _f14, _f15, _f16, ...) _f16
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(F, ...) FUNC_RECOMPOSER((__VA_ARGS__, \
            F##_16, F##_15, F##_14, F##_13, F##_12, F##_11, F##_10, F##_9, F##_8,\
            F##_7, F##_6, F##_5, F##_4, F##_3, F##_2, F##_1, ))
#define NO_ARG_EXPANDER(FUNC) ,,,,,,,,,,,,,,,,FUNC ## _0
#define MACRO_CHOOSER(FUNC, ...) CHOOSE_FROM_ARG_COUNT(FUNC, NO_ARG_EXPANDER __VA_ARGS__ (FUNC))
#define MULTI_MACRO(FUNC, ...) MACRO_CHOOSER(FUNC, __VA_ARGS__)(__VA_ARGS__)

// When you need to make a macro with default arguments, use this:
#define create(...) MULTI_MACRO(CREATE, __VA_ARGS__)
#define CREATE_0() CREATE_1(0)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_2(x, y) \
    do { \
        /* put whatever code you want in the last macro */ \
        realCreate(x, y); \
    } while(0)


int main()
{
    create();
    create(10);
    create(20, 20);
    //create(30, 30, 30);  // Compilation error
    return 0;
}

위의 예제 (Derek Ledbetter, David Sorkovsky 및 Joe D) 중 매크로로 인수를 계산하는 것은 Microsoft VCC 10을 사용하여 저에게 효과적이었습니다. __VA_ARGS__인수는 항상 단일 인수로 간주됩니다 (토큰 화 ##여부에 관계 없음). 이러한 예제가 의존하는 인수 이동은 작동하지 않습니다.

따라서 위의 다른 많은 사람들이 언급했듯이 짧은 대답 : 아니요, 매크로를 오버로드하거나 선택적 인수를 사용할 수 없습니다.

참고 URL : https://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros

반응형