C ++ 매크로가 함수처럼 행동하게하려면 어떻게해야합니까?

StackOverflow https://stackoverflow.com/questions/163365

  •  03-07-2019
  •  | 
  •  

문제

어떤 이유로 든 매크로를 작성해야한다고 가정 해 봅시다. MACRO(X,Y). (인라인 함수를 사용할 수없는 이유가 있다고 가정 해 봅시다.) 이 매크로가 반환 값이없는 함수에 대한 호출을 모방하기를 원합니다.


예 1 : 이것은 예상대로 작동해야합니다.

if (x > y)
  MACRO(x, y);
do_something();

예 2 : 컴파일러 오류가 발생하지 않아야합니다.

if (x > y)
  MACRO(x, y);
else
  MACRO(y - x, x - y);

예 3 : 이것은해야합니다 ~ 아니다 엮다.

do_something();
MACRO(x, y)
do_something();

매크로를 쓰는 순진한 방법은 다음과 같습니다.

#define MACRO(X,Y)                       \
cout << "1st arg is:" << (X) << endl;    \
cout << "2nd arg is:" << (Y) << endl;    \
cout << "Sum is:" << ((X)+(Y)) << endl;

이것은 세 가지 예제 모두에 실패하는 매우 나쁜 해결책이며, 이유를 설명 할 필요는 없습니다.

매크로가 실제로하는 일을 무시하십시오. 그것은 요점이 아닙니다.


이제 내가 가장 자주 쓰여진 매크로를 보는 방식은 다음과 같이 곱슬 괄호 안에 둘러싸여있는 것입니다.

#define MACRO(X,Y)                         \
{                                          \
  cout << "1st arg is:" << (X) << endl;    \
  cout << "2nd arg is:" << (Y) << endl;    \
  cout << "Sum is:" << ((X)+(Y)) << endl;  \
}

매크로가 하나의 명령문 블록에 있기 때문에 예제 1을 해결합니다. 그러나 매크로로 호출 한 후 세미콜론을 넣었 기 때문에 예 2가 끊어졌습니다. 이것은 컴파일러가 세미콜론이 그 자체로 진술이라고 생각하게 만듭니다. 이는 다른 문이 IF 문에 해당하지 않는다는 것을 의미합니다! 마지막으로, 코드 블록에 세미콜론이 필요하지 않기 때문에 세미콜론이 없더라도 예제 3은 정상을 컴파일합니다.


세 예제를 모두 전달하도록 매크로를 작성하는 방법이 있습니까?


참고 : 나는 내 자신의 답변을 팁을 공유하는 방법, 그러나 누군가가 더 나은 솔루션을 가지고 있다면 여기에 자유롭게 게시하십시오. 내 방법보다 더 많은 표를 얻을 수 있습니다. :)

도움이 되었습니까?

해결책

매크로는 일반적으로 피해야합니다. 항상 인라인 기능을 선호합니다. 소금의 가치가있는 모든 컴파일러는 마치 매크로 인 것처럼 작은 기능을 인라인 할 수 있어야하며 인라인 함수는 네임 스페이스 및 기타 스코프를 존중하고 모든 인수를 한 번 평가할 수 있어야합니다.

매크로 여야하는 경우, while 루프 (이미 제안 된)가 작동하거나 쉼표 운영자를 사용해 볼 수 있습니다.

#define MACRO(X,Y) \
 ( \
  (cout << "1st arg is:" << (X) << endl), \
  (cout << "2nd arg is:" << (Y) << endl), \
  (cout << "3rd arg is:" << ((X) + (Y)) << endl), \
  (void)0 \
 )

그만큼 (void)0 진술이 그 중 하나를 평가하게합니다 void 유형 및 세미콜론 대신 쉼표를 사용하면 독립형이 아닌 진술 내에서 사용할 수 있습니다. 나는 여전히 여러 가지 이유로 인라인 함수를 추천 할 것입니다. MACRO(a++, b++) 증가합니다 a 그리고 b 두 배.

다른 팁

다소 영리한 해결책이 있습니다.

#define MACRO(X,Y)                         \
do {                                       \
  cout << "1st arg is:" << (X) << endl;    \
  cout << "2nd arg is:" << (Y) << endl;    \
  cout << "Sum is:" << ((X)+(Y)) << endl;  \
} while (0)

이제 단일 블록 레벨 문이 있으며, 이는 세미콜론이 뒤 따릅니다. 이것은 세 가지 예 모두에서 예상대로 행동하고 원하는대로 동작합니다.

나는 당신이 "매크로가하는 일을 무시한다"고 말하지만 사람들은 제목을 기반 으로이 질문을 찾아서이 질문을 찾을 것이므로 매크로로 기능을 모방하는 추가 기술에 대한 논의가 필요하다고 생각합니다.

내가 아는 가장 가까운 것은 다음과 같습니다.

#define MACRO(X,Y) \
do { \
    auto MACRO_tmp_1 = (X); \
    auto MACRO_tmp_2 = (Y); \
    using std::cout; \
    using std::endl; \
    cout << "1st arg is:" << (MACRO_tmp_1) << endl;    \
    cout << "2nd arg is:" << (MACRO_tmp_2) << endl;    \
    cout << "Sum is:" << (MACRO_tmp_1 + MACRO_tmp_2) << endl; \
} while(0)

이것은 다음을 수행합니다.

  • 명시된 각 상황에서 올바르게 작동합니다.
  • 각 인수를 정확히 한 번 평가합니다. 이는 기능 호출의 보장 된 기능입니다 (두 경우 모두 해당 표현식에서 예외가 없다고 가정).
  • C ++ 0x에서 "자동"을 사용하여 모든 유형에 따라 작용합니다. 이것은 아직 표준 C ++가 아니지만 단일 평가 규칙에 의해 필요한 TMP 변수를 얻는 다른 방법은 없습니다.
  • 발신자가 원래 매크로가하는 네임 스페이스 STD에서 이름을 가져올 필요는 없지만 함수는 그렇지 않습니다.

그러나 여전히 기능과 다릅니다.

  • 일부는 유효하지 않은 용도에서는 다른 컴파일러 오류 또는 경고를 줄 수 있습니다.
  • x 또는 y에 주변 범위에서 'macro_tmp_1'또는 'macro_tmp_2'의 사용이 포함되어 있으면 잘못됩니다.
  • 네임 스페이스 STD와 관련하여 : 함수는 자체 어휘 컨텍스트를 사용하여 이름을 조회하는 반면 매크로는 호출 사이트의 컨텍스트를 사용합니다. 이와 관련하여 기능처럼 행동하는 매크로를 쓸 방법이 없습니다.
  • 무효 표현식 (예 : 쉼표 솔루션)이 할 수있는 공극 함수의 리턴 표현식으로 사용할 수 없습니다. 이것은 원하는 반환 유형이 무효가 아닌 경우, 특히 lvalue로 사용될 때 더 많은 문제입니다. 그러나 쉼표 솔루션에는 선언문이 포함될 수 없으므로 진술이므로 하나를 선택하거나 ({...}) GNU 확장을 사용하십시오.

다음은 다음과 같습니다 libc6! 살펴 봅니다 /usr/include/x86_64-linux-gnu/bits/byteswap.h, 나는 당신이 찾고 있던 트릭을 찾았습니다.

이전 솔루션에 대한 몇몇 비평가 :

  • KIP의 솔루션은 허용되지 않습니다 표현으로 평가, 결국 종종 필요합니다.
  • Coppro의 솔루션은 허용되지 않습니다 변수 할당 표현식은 분리되어 있지만 표현으로 평가할 수 있습니다.
  • Steve Jessop의 솔루션은 C ++ 11을 사용합니다 auto 키워드, 괜찮습니다 알려진/예상 유형을 자유롭게 사용하십시오 대신에.

트릭은 두 가지를 모두 사용하는 것입니다 (expr,expr) 구성 및 a {} 범위:

#define MACRO(X,Y) \
  ( \
    { \
      register int __x = static_cast<int>(X), __y = static_cast<int>(Y); \
      std::cout << "1st arg is:" << __x << std::endl; \
      std::cout << "2nd arg is:" << __y << std::endl; \
      std::cout << "Sum is:" << (__x + __y) << std::endl; \
      __x + __y; \
    } \
  )

사용에 주목하십시오 register 키워드, 컴파일러에 대한 힌트 일뿐입니다. 그만큼 X 그리고 Y 매크로 매개 변수는 (이미) 괄호 안에 둘러싸여 있으며 캐스트 예상 유형으로. 이 솔루션은 매개 변수가 한 번만 평가되므로 증가 사전 및 사후 증가로 올바르게 작동합니다.

예제 목적을 위해 요청되지 않았지만 __x + __y; 진술은 전체 블록을 그 정확한 표현으로 평가하도록하는 방법입니다.

사용하는 것이 더 안전합니다 void(); 매크로가 표현을 평가하지 않도록하려면 rvalue 예상됩니다.

하지만, 솔루션은입니다 ISO C ++를 준수하지 않습니다 불평 할 것입니다 g++ -pedantic:

warning: ISO C++ forbids braced-groups within expressions [-pedantic]

휴식을 취하기 위해 g++, 사용 (__extension__ OLD_WHOLE_MACRO_CONTENT_HERE) 새로운 정의가 읽습니다.

#define MACRO(X,Y) \
  (__extension__ ( \
    { \
      register int __x = static_cast<int>(X), __y = static_cast<int>(Y); \
      std::cout << "1st arg is:" << __x << std::endl; \
      std::cout << "2nd arg is:" << __y << std::endl; \
      std::cout << "Sum is:" << (__x + __y) << std::endl; \
      __x + __y; \
    } \
  ))

내 솔루션을 조금 더 개선하기 위해 __typeof__ 키워드 c:

#define MACRO(X,Y) \
  (__extension__ ( \
    { \
      __typeof__(X) __x = (X); \
      __typeof__(Y) __y = (Y); \
      std::cout << "1st arg is:" << __x << std::endl; \
      std::cout << "2nd arg is:" << __y << std::endl; \
      std::cout << "Sum is:" << (__x + __y) << std::endl; \
      __x + __y; \
    } \
  ))

이제 컴파일러가 적절한 유형을 결정합니다. 이것도 A입니다 gcc 확대.

제거에 유의하십시오 register 클래스 유형과 함께 사용할 때 다음 경고와 같이 키워드 :

warning: address requested for ‘__x’, which is declared ‘register’ [-Wextra]

C ++ 11은 우리에게 람다를 가져 왔는데,이 상황에서 매우 유용 할 수 있습니다.

#define MACRO(X,Y)                              \
    [&](x_, y_) {                               \
        cout << "1st arg is:" << x_ << endl;    \
        cout << "2nd arg is:" << y_ << endl;    \
        cout << "Sum is:" << (x_ + y_) << endl; \
    }((X), (Y))

당신은 매크로의 생성력을 유지하지만 원하는 것을 반환 할 수있는 편안한 범위를 가지고 있습니다. void). 또한, 매크로 매개 변수를 여러 번 평가하는 문제를 피합니다.

사용중인 블록을 만듭니다

 #define MACRO(...) do { ... } while(false)

A를 추가하지 마십시오. 잠시 후 (거짓)

귀하의 답변은 다중 평가 문제로 고통받습니다.

macro( read_int(file1), read_int(file2) );

예상치 못한 일을하고 원치 않는 일을 할 것입니다.

다른 사람들이 언급했듯이 가능할 때마다 매크로를 피해야합니다. 거시적 논증이 두 번 이상 평가되면 부작용이있을 때 위험합니다. 인수의 유형을 알고 있다면 (또는 C ++ 0x를 사용할 수 있습니다. auto 기능), 임시를 사용하여 단일 평가를 시행 할 수 있습니다.

또 다른 문제 : 여러 평가가 발생하는 순서는 당신이 기대하지 않을 수 있습니다!

이 코드를 고려하십시오.

#include <iostream>
using namespace std;

int foo( int & i ) { return i *= 10; }
int bar( int & i ) { return i *= 100; }

#define BADMACRO( X, Y ) do { \
    cout << "X=" << (X) << ", Y=" << (Y) << ", X+Y=" << ((X)+(Y)) << endl; \
    } while (0)

#define MACRO( X, Y ) do { \
    int x = X; int y = Y; \
    cout << "X=" << x << ", Y=" << y << ", X+Y=" << ( x + y ) << endl; \
    } while (0)

int main() {
    int a = 1; int b = 1;
    BADMACRO( foo(a), bar(b) );
    a = 1; b = 1;
    MACRO( foo(a), bar(b) );
    return 0;
}

그리고 컴파일 된대로 출력 및 내 컴퓨터에서 실행됩니다.

X=100, Y=10000, X+Y=110
X=10, Y=100, X+Y=110

if 문장에서 항상 Curly Brace를 사용하는 관행을 기꺼이 채택하려는 경우

당신의 매크로는 단순히 마지막 세미콜론을 놓치게 될 것입니다.

#define MACRO(X,Y)                       \
cout << "1st arg is:" << (X) << endl;    \
cout << "2nd arg is:" << (Y) << endl;    \
cout << "Sum is:" << ((X)+(Y)) << endl

예 1 : (컴파일)

if (x > y) {
    MACRO(x, y);
}
do_something();

예 2 : (컴파일)

if (x > y) {
    MACRO(x, y);
} else {
    MACRO(y - x, x - y);
}

예 3 : (컴파일하지 않음)

do_something();
MACRO(x, y)
do_something();
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top