C ++ 매크로는 언제 유익합니까? [닫은]


177

C의 전처리는 정당 두려워하고 C ++ 커뮤니티에 의해 기피한다. 인라인 함수, const 및 템플릿은 일반적으로 a보다 안전하고 우수한 대안 #define입니다.

다음 매크로 :

#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)  

타입 안전보다 우월하지 않습니다.

inline bool succeeded(int hr) { return hr >= 0; }

그러나 매크로는 그 자리에 있습니다 . 전 처리기 없이는 할 수없는 매크로에 대한 용도를 나열하십시오 .

각 유스 케이스를 별도의 답변으로 작성하여 투표에 참여할 수 있도록하고, 전임없이 답변 중 하나를 달성하는 방법을 알고있는 경우, 해당 답변의 의견에 대한 답변을 제시하십시오.


한 번은 매크로로 가득 찬 C ++ 응용 프로그램을 작성하여 45 분이 걸리고 매크로를 인라인 함수로 바꾸고 빌드를 15 분 미만으로 줄였습니다.
endian


스레드는 매크로가 최적이 아닌 컨텍스트가 아니라 매크로가 유익한 컨텍스트에 관한 것입니다.
underscore_d

답변:


123

디버그 기능에 대한 래퍼로, 자동으로 같은 것들을 통과 __FILE__, __LINE__등 :

#ifdef ( DEBUG )
#define M_DebugLog( msg )  std::cout << __FILE__ << ":" << __LINE__ << ": " << msg
#else
#define M_DebugLog( msg )
#endif

14
실제로, 원래 스 니펫 : << FILE ":"<<은 괜찮습니다. FILE 은 문자열 상수를 생성하며, ":"와 함께 전처리기에 의해 단일 문자열로 연결됩니다.
Frank Szczerba 2016 년

12
이것은 단지 때문에 전처리를 필요로 __FILE__하고 __LINE__ 또한 전처리가 필요합니다. 코드에서 그것들을 사용하는 것은 전 처리기의 감염 벡터와 같습니다.
TED

93

메소드는 항상 완전하고 컴파일 가능한 코드 여야합니다. 매크로는 코드 조각 일 수 있습니다. 따라서 foreach 매크로를 정의 할 수 있습니다.

#define foreach(list, index) for(index = 0; index < list.size(); index++)

다음과 같이 사용하십시오.

foreach(cookies, i)
    printf("Cookie: %s", cookies[i]);

C ++ 11부터는 범위 기반 for 루프로 대체되었습니다 .


6
+1 엄청나게 복잡한 반복자 구문을 사용하는 경우 foreach 스타일 매크로를 작성하면 코드를 훨씬 쉽게 읽고 유지 관리 할 수 ​​있습니다. 내가 해냈어, 효과가있어.
postfuturist

9
대부분의 주석은 매크로가 완전한 코드 대신 코드 조각 일 수 있다는 점과는 전혀 관련이 없습니다. 그러나 nitpicking 주셔서 감사합니다.
jdmichal

12
이것은 C ++가 아닌 C입니다. C ++를 수행하는 경우 반복자와 std :: for_each를 사용해야합니다.
chrish 2016 년

20
동의하지 않습니다. 람다 이전 for_each에는 각 요소가 실행되는 코드가 호출 지점에 국한되지 않았기 때문에 불쾌한 일이었습니다. foreach, ( BOOST_FOREACH수동 솔루션 대신 강력히 권장 합니다) 코드를 반복 사이트에 가깝게 유지하여 더 읽기 쉽도록하겠습니다. 람다가 출시 for_each되면 다시 갈 길이 될 수 있습니다.
GManNickG

8
그리고 그것의 가치는 BOOST_FOREACH가 자체 매크로 것을 지적 (하지만 아주 면밀한 하나)
타일러 맥 헨리

59

헤더 파일 가드는 매크로를 필요로합니다.

매크로 가 필요한 다른 영역이 있습니까? 많지 않은 경우.

매크로로 혜택을 보는 다른 상황이 있습니까? 예!!!

매크로를 사용하는 곳은 매우 반복적 인 코드입니다. 예를 들어 다른 인터페이스 (.NET, COM, Python 등)와 함께 사용할 C ++ 코드를 래핑 할 때는 다른 유형의 예외를 잡아야합니다. 내가하는 방법은 다음과 같습니다.

#define HANDLE_EXCEPTIONS \
catch (::mylib::exception& e) { \
    throw gcnew MyDotNetLib::Exception(e); \
} \
catch (::std::exception& e) { \
    throw gcnew MyDotNetLib::Exception(e, __LINE__, __FILE__); \
} \
catch (...) { \
    throw gcnew MyDotNetLib::UnknownException(__LINE__, __FILE__); \
}

이 캐치를 모든 래퍼 함수에 넣어야합니다. 매번 전체 catch 블록을 입력하는 대신 다음을 입력하십시오.

void Foo()
{
    try {
        ::mylib::Foo()
    }
    HANDLE_EXCEPTIONS
}

또한 유지 보수가 쉬워집니다. 새로운 예외 유형을 추가해야하는 경우 추가해야 할 곳이 한 곳뿐입니다.

다른 유용한 사례도 있습니다 많은의 포함 __FILE____LINE__전처리 매크로.

어쨌든 매크로는 올바르게 사용될 때 매우 유용합니다. 매크로는 악이 아닙니다 . 오용 은 악입니다.


7
대부분의 컴파일러는 #pragma once요즘을 지원 하므로 경비원이 정말로 필요한지 의심됩니다.
1800 INFORMATION

13
대부분의 컴파일러 대신 모든 컴파일러를 작성한다면 ;-)
Steve Jessop

30
따라서 휴대용 표준 전 처리기 기능 대신 전 처리기 확장을 사용하여 전처리기를 사용하지 않는 것이 좋습니다. 나에게 우스운 것 같습니다.
로간 카파도

#pragma once 많은 일반적인 빌드 시스템을 손상시킵니다.
Miles Rout

4
매크로가 필요없는 솔루션이 있습니다 void handleExceptions(){ try { throw } catch (::mylib::exception& e) {....} catch (::std::exception& e) {...} ... }. 그리고 기능 측면에서 :void Foo(){ try {::mylib::Foo() } catch (...) {handleExceptions(); } }
MikeMB

51

대개:

  1. 경비원 포함
  2. 조건부 컴파일
  3. 같은보고 (사전 정의 된 매크로 __LINE____FILE__ )
  4. (드물게) 반복적 인 코드 패턴 복제.
  5. 경쟁사의 코드에서.

5 번을 실현하는 방법에 대한 도움이 필요하십니까?
최대

50

컴파일러 간의 차이점 문제를 극복하기위한 내부 조건부 컴파일 :

#ifdef ARE_WE_ON_WIN32
#define close(parm1)            _close (parm1)
#define rmdir(parm1)            _rmdir (parm1)
#define mkdir(parm1, parm2)     _mkdir (parm1)
#define access(parm1, parm2)    _access(parm1, parm2)
#define create(parm1, parm2)    _creat (parm1, parm2)
#define unlink(parm1)           _unlink(parm1)
#endif

12
C ++에서 인라인 함수를 사용하여 동일하게 얻을 수 있습니다. <code> #ifdef ARE_WE_ON_WIN32 <br> inline int close (int i) {return _close (i); } <br> #endif </ code>
paercebal

2
#define은 제거하지만 #ifdef 및 #endif는 제거되지 않습니다. 어쨌든, 나는 당신에 동의합니다.
Gorpik

19
절대로 소문자 매크로를 정의하지 마십시오. 기능을 변경하는 매크로는 악몽입니다 (Microsoft에게 감사합니다). 가장 좋은 예는 첫 줄입니다. 많은 라이브러리에는 close함수 나 메소드가 있습니다. 그런 다음 큰 문제가있는 것보다이 라이브러리의 헤더와이 매크로가있는 헤더를 포함 시키면 라이브러리 API를 사용할 수 없습니다.
Marek R

AndrewStein, @paercebal의 제안에 비해이 맥락에서 매크로를 사용하면 어떤 이점이 있습니까? 그렇지 않은 경우 매크로가 실제로 무상 인 것 같습니다.
einpoklum

1
#ifdef WE_ARE_ON_WIN32plz :)
궤도에서 가벼움 경주

38

식에서 문자열을 만들려는 경우 가장 좋은 예는 assert( #x값을 x문자열로 바꿉니다 ).

#define ASSERT_THROW(condition) \
if (!(condition)) \
     throw std::exception(#condition " is false");

5
단지 nitpick이지만, 나는 개인적으로 세미콜론을 남겨 두었습니다.
Michael Myers

10
사실, 나는 {} while (false) (높은 재킹을 방지하기 위해) do에 넣을 것이라고 동의하지만 간단하게 유지하고 싶었습니다.
Motti

33

문자열 상수는 매크로보다 문자열 리터럴로 더 많은 작업을 수행 할 수 있으므로 매크로로 더 잘 정의되는 경우가 있습니다 const char *.

예를 들어 문자열 리터럴을 쉽게 연결할 수 있습니다 .

#define BASE_HKEY "Software\\Microsoft\\Internet Explorer\\"
// Now we can concat with other literals
RegOpenKey(HKEY_CURRENT_USER, BASE_HKEY "Settings", &settings);
RegOpenKey(HKEY_CURRENT_USER, BASE_HKEY "TypedURLs", &URLs);

a const char *가 사용 된 경우 런타임에 연결을 수행하려면 일종의 문자열 클래스를 사용해야합니다.

const char* BaseHkey = "Software\\Microsoft\\Internet Explorer\\";
RegOpenKey(HKEY_CURRENT_USER, (string(BaseHkey) + "Settings").c_str(), &settings);
RegOpenKey(HKEY_CURRENT_USER, (string(BaseHkey) + "TypedURLs").c_str(), &URLs);

2
C ++ 11에서는 이것이 가장 중요한 부분이라고 생각합니다 (가드 포함). 매크로는 실제로 컴파일 타임 문자열 처리에 가장 좋은 것입니다. 이것이 C ++ 11 ++에서 얻을 수있는 기능입니다
David Stone

1
이것이 C #에서 매크로를 원했던 상황입니다.
Rawling

2
나는 이것을 +42 할 수 있기를 바랍니다. 문자열 리터럴의 측면을 자주 기억하지는 않지만 매우 중요합니다.
Daniel Kamil Kozar

24

함수 의 프로그램 흐름 ( return, breakcontinue) 코드 를 변경하려는 경우 실제로 함수에 인라인 된 코드와 다르게 작동합니다.

#define ASSERT_RETURN(condition, ret_val) \
if (!(condition)) { \
    assert(false && #condition); \
    return ret_val; }

// should really be in a do { } while(false) but that's another discussion.

예외를 던지는 것이 더 나은 대안처럼 보입니다.
einpoklum

(++) 확장 파이썬 C를 작성할 때, 예외는 예외 문자열을 설정 반환하여 전파됩니다 -1NULL. 따라서 매크로는 상용구 코드를 크게 줄일 수 있습니다.
black_puppydog

20

명백한 경비원

#ifndef MYHEADER_H
#define MYHEADER_H

...

#endif

17

일반 함수 호출을 사용하여 함수 호출 인수의 단락을 수행 할 수 없습니다. 예를 들면 다음과 같습니다.

#define andm(a, b) (a) && (b)

bool andf(bool a, bool b) { return a && b; }

andm(x, y) // short circuits the operator so if x is false, y would not be evaluated
andf(x, y) // y will always be evaluated

3
좀 더 일반적인 요점 : 함수는 인수를 정확히 한 번 평가합니다. 매크로는 인수를 여러 번 또는 더 적게 평가할 수 있습니다.
Steve Jessop

@ [Greg Rogers] 모든 매크로 프리 프로세서는 대체 텍스트입니다. 일단 당신이 그것을 이해하면 그것에 대해 더 이상 신비가 없어야합니다.
1800 정보

함수를 호출하기 전에 평가를 강제로 취소하는 대신 andf를 템플릿 화하여 동등한 동작을 얻을 수 있습니다. 나는 당신이 직접 시도하지 않고 당신이 말한 것이 사실임을 깨닫지 못했을 것입니다. 흥미 롭군
Greg Rogers

템플릿으로 정확히 어떻게 할 수 있습니까?
1800 정보

6
함수 스타일 매크로 뒤에 단락 작업을 숨기는 것은 실제로 프로덕션 코드에서보고 싶지 않은 것 중 하나입니다.
MikeMB

17

헤더 가드와 같은 명백한 것을 무시한다고 가정 해 봅시다.

때로는 프리 컴파일러가 복사 / 붙여 넣기해야하는 코드를 생성하려고합니다.

#define RAISE_ERROR_STL(p_strMessage)                                          \
do                                                                             \
{                                                                              \
   try                                                                         \
   {                                                                           \
      std::tstringstream strBuffer ;                                           \
      strBuffer << p_strMessage ;                                              \
      strMessage = strBuffer.str() ;                                           \
      raiseSomeAlert(__FILE__, __FUNCSIG__, __LINE__, strBuffer.str().c_str()) \
   }                                                                           \
   catch(...){}                                                                \
   {                                                                           \
   }                                                                           \
}                                                                              \
while(false)

이것을 코딩 할 수 있습니다 :

RAISE_ERROR_STL("Hello... The following values " << i << " and " << j << " are wrong") ;

다음과 같은 메시지를 생성 할 수 있습니다.

Error Raised:
====================================
File : MyFile.cpp, line 225
Function : MyFunction(int, double)
Message : "Hello... The following values 23 and 12 are wrong"

템플릿을 매크로와 혼합하면 더 나은 결과를 얻을 수 있습니다 (예 : 변수 이름과 나란히 값을 자동으로 생성)

다른 경우, 예를 들어 디버그 정보를 생성하려면 일부 코드의 __FILE__ 및 / 또는 __LINE__이 필요합니다. 다음은 Visual C ++의 고전입니다.

#define WRNG_PRIVATE_STR2(z) #z
#define WRNG_PRIVATE_STR1(x) WRNG_PRIVATE_STR2(x)
#define WRNG __FILE__ "("WRNG_PRIVATE_STR1(__LINE__)") : ------------ : "

다음 코드와 같이

#pragma message(WRNG "Hello World")

다음과 같은 메시지를 생성합니다.

C:\my_project\my_cpp_file.cpp (225) : ------------ Hello World

다른 경우에는 속성에 대한 게터 및 세터 생성과 같이 # 및 ## 연결 연산자를 사용하여 코드를 생성해야합니다 (이 경우는 매우 제한적입니다).

다른 경우에는 다음과 같은 함수를 통해 사용하면 컴파일되지 않는 코드가 생성됩니다.

#define MY_TRY      try{
#define MY_CATCH    } catch(...) {
#define MY_END_TRY  }

어느 것으로 사용할 수

MY_TRY
   doSomethingDangerous() ;
MY_CATCH
   tryToRecoverEvenWithoutMeaningfullInfo() ;
   damnThoseMacros() ;
MY_END_TRY

(아직도 한 번만 올바르게 사용되는 이러한 종류의 코드 만 보았습니다 )

마지막으로, 유명한 boost::foreach!!!

#include <string>
#include <iostream>
#include <boost/foreach.hpp>

int main()
{
    std::string hello( "Hello, world!" );

    BOOST_FOREACH( char ch, hello )
    {
        std::cout << ch;
    }

    return 0;
}

(참고 : 부스트 홈페이지에서 코드 복사 / 붙여 넣기)

어느 것이 (IMHO)보다 낫습니다 std::for_each.

따라서 매크로는 일반적인 컴파일러 규칙 외부에 있기 때문에 항상 유용합니다. 그러나 나는 하나를 볼 때 대부분 C 코드가 실제로 유효한 C ++로 변환되지 않은 채 남아 있다는 것을 알았습니다.


1
컴파일러가 수행 할 수없는 작업에만 CPP를 사용하십시오. 예를 들어, RAISE_ERROR_STL은 CPP를 사용하여 파일, 라인 및 함수 서명을 판별하고 나머지를 수행하는 함수 (인라인으로)에 전달해야합니다.
Rainer Blome

C ++ 11을 반영하여 답변을 업데이트하고 @RainerBlome의 의견을 보내주십시오.
einpoklum

@RainerBlome : 우리는 동의합니다. RAISE_ERROR_STL 매크로는 C ++ 11 이전 버전이므로 해당 컨텍스트에서 완전히 정당화됩니다. 내 이해 (그러나 특정 기능을 처리 할 기회가 없었 음)는 Modern C ++에서 가변 템플릿 (또는 매크로?)을 사용하여 문제를보다 우아하게 해결할 수 있다는 것입니다.
paercebal

@einpoklum : "C ++ 11을 반영하고 RainerBlome 님의 의견을 반영하여 답변을 업데이트하십시오": :-). . . 필자는 현대적인 C ++에 대한 섹션을 추가하여 매크로의 필요성을 줄이거 나 없애는 대안 구현을 생각하지만 요점은 다음과 같습니다. 매크로는 못 생겼고 악하지만 컴파일러가 이해하지 못하는 것이 있습니다. 매크로를 통해 수행합니다.
paercebal

C ++ 11에서도 함수가 수행 할 수있는 많은 매크로를 남겨 둘 수 있습니다. #include <sstream> #include <iostream> using namespace std; void trace(char const * file, int line, ostream & o) { cerr<<file<<":"<<line<<": "<< static_cast<ostringstream & >(o).str().c_str()<<endl; } struct Oss { ostringstream s; ostringstream & lval() { return s; } }; #define TRACE(ostreamstuff) trace(__FILE__, __LINE__, Oss().lval()<<ostreamstuff) int main() { TRACE("Hello " << 123); return 0; }그렇게하면 매크로가 더 짧아집니다.
Rainer Blome

16

UnitTest ++ 와 같은 C ++의 단위 테스트 프레임 워크는 전 처리기 매크로를 중심으로 진행됩니다. 몇 줄의 단위 테스트 코드는 수동으로 입력하기가 전혀 재미없는 클래스 계층으로 확장됩니다. UnitTest ++와 같은 것이없고 전 처리기 마법이기 때문에 C ++에 대한 단위 테스트를 어떻게 효율적으로 작성하는지 모르겠습니다.


단위 테스트는 프레임 워크 없이도 완벽하게 작성할 수 있습니다. 결국, 그것은 실제로 어떤 종류의 출력을 원하는지에 달려 있습니다. 신경 쓰지 않으면 성공 또는 실패를 나타내는 간단한 종료 값이 완벽하게 좋습니다.
더 명확한

15

C 전처리기를 두려워하는 것은 형광등을 얻기 때문에 백열등을 두려워하는 것과 같습니다. 예, 전자는 {전기 | 프로그래머 시간}이 비효율적입니다. 예, 당신은 그들에 의해 (문자 그대로) 화상을 입을 수 있습니다. 그러나 제대로 처리하면 업무를 수행 할 수 있습니다.

임베디드 시스템을 프로그래밍 할 때 C는 유일한 옵션 구분 형식 어셈블러입니다. C ++로 데스크톱에서 프로그래밍 한 다음 더 작은 임베디드 대상으로 전환 한 후에는 수많은 C 기능 (매크로 포함)의 "무효 성"에 대한 걱정을 멈추고 그로부터 얻을 수있는 가장 안전하고 안전한 사용법을 알아내는 것을 배우게됩니다 풍모.

알렉산더 스테파노 프 말한다 :

C ++로 프로그래밍 할 때 C 유산을 부끄러워하지 말고 완전히 활용하십시오. C ++의 유일한 문제, 심지어 C의 유일한 문제는 자체 논리와 일치하지 않을 때 발생합니다.


나는 이것이 잘못된 태도라고 생각합니다. "적절하게 처리"하는 법을 배울 수 있다고해서 다른 사람의 시간과 노력을 들일 가치가있는 것은 아닙니다.
Neil G

9

QA 인프라의 자동화 된 로그 파일 스캐너와 함께 정보가 풍부한 예외 발생, 포착 및 로깅에서 진단 목적으로 __FILE____LINE__매크로를 사용합니다 .

예를 들어, OUR_OWN_THROW텍스트 설명을 포함하여 해당 매크로에 대한 예외 유형 및 생성자 매개 변수와 함께 throw 매크로 가 사용될 수 있습니다. 이처럼 :

OUR_OWN_THROW(InvalidOperationException, (L"Uninitialized foo!"));

이 매크로는 물론 InvalidOperationException설명을 생성자 매개 변수로 사용 하여 예외를 throw 하지만 파일 이름과 줄 번호 및 텍스트 설명으로 구성된 로그 파일에도 메시지를 씁니다. 발생 된 예외는 ID를 가져 오며 기록됩니다. 예외가 코드의 다른 곳에서 발견되면 예외로 표시되고 로그 파일은 특정 예외가 처리되었으며 나중에 로그온 할 수있는 충돌의 원인이 아님을 나타냅니다. 자동화 된 QA 인프라를 통해 처리되지 않은 예외를 쉽게 해결할 수 있습니다.


9

코드 반복.

전 처리기 라이브러리향상 시키는 일종의 메타 메타 프로그래밍입니다. 주제-> 동기 부여에서 좋은 예를 찾을 수 있습니다.


모든 경우는 아니지만 거의 모든 경우-함수 호출로 코드 반복을 피할 수 있습니다.
einpoklum

@einpoklum : 동의하지 않습니다. 링크를보십시오
Ruggero Turra

9

전 처리기 (매크로)를 사용하여 매우 고급스럽고 유용한 것들을 만들 수 있는데, 템플릿을 포함한 c ++ "언어 구성"을 사용하면 절대 불가능합니다.

예 :

C 식별자와 문자열을 모두 만들기

C에서 열거 형 유형의 변수를 문자열로 사용하는 쉬운 방법

전 처리기 메타 프로그래밍 향상


세 번째 링크를 참고하시기 바랍니다 고장
로빈 Hartland의

좀 가지고 stdio.hsal.h의 파일을 vc12더 잘 이해하기위한합니다.
Elshan

7

때때로 매크로를 사용하여 한 곳에서 정보를 정의 할 수 있지만 코드의 다른 부분에서 다른 방식으로 정보를 사용합니다. 약간 악하다 :)

예를 들어 "field_list.h"에서

/*
 * List of fields, names and values.
 */
FIELD(EXAMPLE1, "first example", 10)
FIELD(EXAMPLE2, "second example", 96)
FIELD(ANOTHER, "more stuff", 32)
...
#undef FIELD

그런 다음 공개 열거 형의 경우 이름을 사용하도록 정의 할 수 있습니다.

#define FIELD(name, desc, value) FIELD_ ## name,

typedef field_ {

#include "field_list.h"

    FIELD_MAX

} field_en;

개인 초기화 함수에서 모든 필드를 사용하여 테이블을 데이터로 채울 수 있습니다.

#define FIELD(name, desc, value) \
    table[FIELD_ ## name].desc = desc; \
    table[FIELD_ ## name].value = value;

#include "field_list.h"

1
참고 : 별도의 포함 없이도 유사한 기술을 구현할 수 있습니다. 참조 : stackoverflow.com/questions/147267/… stackoverflow.com/questions/126277/…
Suma

6

하나의 일반적인 용도는 컴파일 환경을 감지하는 것입니다. 크로스 플랫폼 개발의 경우, 목적에 맞는 크로스 플랫폼 라이브러리가없는 경우 Linux 용 코드 세트와 Windows 용 코드 세트를 작성할 수 있습니다.

대략적인 예에서 크로스 플랫폼 뮤텍스는

void lock()
{
    #ifdef WIN32
    EnterCriticalSection(...)
    #endif
    #ifdef POSIX
    pthread_mutex_lock(...)
    #endif
}

함수의 경우 형식 안전을 명시 적으로 무시하려는 경우에 유용합니다. ASSERT를 수행하기위한 위와 아래의 많은 예와 같습니다. 물론 많은 C / C ++ 기능과 마찬가지로 발로 직접 촬영할 수 있지만 언어는 도구를 제공하고 수행 할 작업을 결정할 수 있습니다.


질문자가 묻은 이후 : 플랫폼마다 다른 포함 경로를 통해 다른 헤더를 포함하여 매크로없이 수행 할 수 있습니다. 매크로가 종종 더 편리하지만 동의하는 경향이 있습니다.
Steve Jessop

나는 두 번째입니다. 이러한 목적으로 매크로를 사용하기 시작하면 코드를 쉽게 읽을 수 없게됩니다.
Nemanja Trifunovic

6

같은 것

void debugAssert(bool val, const char* file, int lineNumber);
#define assert(x) debugAssert(x,__FILE__,__LINE__);

예를 들어

assert(n == true);

n이 false 인 경우 소스 파일 이름과 문제의 줄 번호를 로그에 인쇄하십시오.

다음과 같은 일반 함수 호출을 사용하는 경우

void assert(bool val);

매크로 대신에 어설 션 함수의 줄 번호를 로그에 인쇄하면 유용하지 않습니다.


표준 라이브러리의 구현은 이미를 통해 제공 할 때 왜 바퀴를 재발견 할 파일 / 라인 / 기능 정보를 덤프 매크로,? (어쨌든 본 모든 구현에서)<cassert>assert()
underscore_d

4
#define ARRAY_SIZE(arr) (sizeof arr / sizeof arr[0])

현재 스레드에서 논의 된 '선호'템플릿 솔루션과 달리 상수 표현식으로 사용할 수 있습니다.

char src[23];
int dest[ARRAY_SIZE(src)];

2
이것은 (배열 대신 포인터를 전달하는 경우 컴파일되지 않을 것이다) 안전한 방법으로 템플릿을 수행 할 수 있습니다 stackoverflow.com/questions/720077/calculating-size-of-an-array/...
은 Motti

1
이제 C ++ 11에 constexpr이 있으므로 안전한 (매크로가 아닌) 버전도 상수 표현식에 사용할 수 있습니다. template<typename T, std::size_t size> constexpr std::size_t array_size(T const (&)[size]) { return size; }
David Stone

3

#defines를 사용하여 디버깅 및 단위 테스트 시나리오를 도울 수 있습니다. 예를 들어, 메모리 함수의 특수 로깅 변형을 작성하고 특수 memlog_preinclude.h를 작성하십시오.

#define malloc memlog_malloc
#define calloc memlog calloc
#define free memlog_free

다음을 사용하여 코드를 컴파일하십시오.

gcc -Imemlog_preinclude.h ...

최종 이미지에 대한 memlog.o의 링크. 이제 로깅 목적으로 또는 단위 테스트에 대한 할당 실패를 시뮬레이션하기 위해 malloc 등을 제어합니다.


3

컴파일러 / OS / 하드웨어 특정 동작에 대해 컴파일 타임에 결정을 내릴 때.

Comppiler / OS / Hardware 특정 기능에 대한 인터페이스를 만들 수 있습니다.

#if defined(MY_OS1) && defined(MY_HARDWARE1)
#define   MY_ACTION(a,b,c)      doSothing_OS1HW1(a,b,c);}
#elif define(MY_OS1) && defined(MY_HARDWARE2)
#define   MY_ACTION(a,b,c)      doSomthing_OS1HW2(a,b,c);}
#elif define(MY_SUPER_OS)
          /* On this hardware it is a null operation */
#define   MY_ACTION(a,b,c)
#else
#error  "PLEASE DEFINE MY_ACTION() for this Compiler/OS/HArdware configuration"
#endif

3

매크로를 사용하여 예외를 쉽게 정의합니다.

DEF_EXCEPTION(RessourceNotFound, "Ressource not found")

여기서 DEF_EXCEPTION은

#define DEF_EXCEPTION(A, B) class A : public exception\
  {\
  public:\
    virtual const char* what() const throw()\
    {\
      return B;\
    };\
  }\

2

컴파일러는 인라인 요청을 거부 할 수 있습니다.

매크로는 항상 자리를 차지할 것입니다.

내가 유용하게 생각하는 것은 디버그 추적을위한 #define DEBUG입니다. 문제를 디버깅하는 동안 1을 그대로두고 (또는 전체 개발주기 동안 그대로 두어) 배송 할 때 끌 수 있습니다.


10
컴파일러가 인라인 요청을 거부하면 그럴만한 이유가있을 수 있습니다. 좋은 컴파일러는 사용자보다 올바르게 인라인하는 것이 좋으며, 나쁜 컴파일러는 이보다 더 많은 성능 문제를 야기합니다.
David Thornley

@DavidThornley 아니면 GCC 또는 CLANG / LLVM과 같은 훌륭한 최적화 컴파일러가 아닐 수도 있습니다. 일부 컴파일러는 쓰레기입니다.
Miles Rout

2

마지막 직장에서 바이러스 스캐너를 연구하고있었습니다. 디버깅하기가 더 쉬워 지도록 많은 곳에서 로깅이 많이 발생했지만 그런 수요가 많은 앱에서는 함수 호출 비용이 너무 비쌉니다. 그래서, 나는이 작은 매크로를 생각해 냈습니다.이 기능 호출 비용없이 디버그 플래그를 확인하고 아무것도 로깅하지 않고 또는 활성화 된 경우 반환하는 고객 사이트의 릴리스 버전에서 디버그 로깅을 계속 활성화 할 수있었습니다. , 로깅을 수행합니다 ... 매크로는 다음과 같이 정의되었습니다.

#define dbgmsg(_FORMAT, ...)  if((debugmsg_flag  & 0x00000001) || (debugmsg_flag & 0x80000000))     { log_dbgmsg(_FORMAT, __VA_ARGS__);  }

로그 함수의 VA_ARGS로 인해 이와 같은 매크로에 적합합니다.

그 전에는 높은 보안 응용 프로그램에서 매크로를 사용하여 사용자에게 올바른 액세스 권한이 없음을 알리고 필요한 플래그를 알려줍니다.

매크로는 다음과 같이 정의됩니다.

#define SECURITY_CHECK(lRequiredSecRoles) if(!DoSecurityCheck(lRequiredSecRoles, #lRequiredSecRoles, true)) return
#define SECURITY_CHECK_QUIET(lRequiredSecRoles) (DoSecurityCheck(lRequiredSecRoles, #lRequiredSecRoles, false))

그런 다음 UI 전체에 수표를 뿌릴 수 있으며, 해당 역할이없는 경우 수행하려는 작업을 수행 할 수있는 역할을 알려줍니다. 그중 두 가지 이유는 어떤 곳에서는 값을 반환하고 다른 곳에서는 void 함수에서 돌아 오는 것이 었습니다 ...

SECURITY_CHECK(ROLE_BUSINESS_INFORMATION_STEWARD | ROLE_WORKER_ADMINISTRATOR);

LRESULT CAddPerson1::OnWizardNext() 
{
   if(m_Role.GetItemData(m_Role.GetCurSel()) == parent->ROLE_EMPLOYEE) {
      SECURITY_CHECK(ROLE_WORKER_ADMINISTRATOR | ROLE_BUSINESS_INFORMATION_STEWARD ) -1;
   } else if(m_Role.GetItemData(m_Role.GetCurSel()) == parent->ROLE_CONTINGENT) {
      SECURITY_CHECK(ROLE_CONTINGENT_WORKER_ADMINISTRATOR | ROLE_BUSINESS_INFORMATION_STEWARD | ROLE_WORKER_ADMINISTRATOR) -1;
   }
...

어쨌든, 그것은 내가 그것을 사용한 방법이며, 이것이 템플릿에 어떻게 도움이 될 수 있는지 잘 모르겠습니다 ... 그 외에는 정말로 필요하지 않으면 피하지 마십시오.


2

또 다른 foreach 매크로. T : 유형, c : 컨테이너, i : 반복자

#define foreach(T, c, i) for(T::iterator i=(c).begin(); i!=(c).end(); ++i)
#define foreach_const(T, c, i) for(T::const_iterator i=(c).begin(); i!=(c).end(); ++i)

사용법 (실제가 아닌 개념 표시) :

void MultiplyEveryElementInList(std::list<int>& ints, int mul)
{
    foreach(std::list<int>, ints, i)
        (*i) *= mul;
}

int GetSumOfList(const std::list<int>& ints)
{
    int ret = 0;
    foreach_const(std::list<int>, ints, i)
        ret += *i;
    return ret;
}

더 나은 구현 가능 : Google "BOOST_FOREACH"

유용한 기사 : 조건부 사랑 : FOREACH Redux (Eric Niebler) http://www.artima.com/cppsource/foreach.html


2

매크로의 대단한 사용법은 플랫폼 독립적 인 개발 일 것입니다. 유형 불일치 사례를 생각해보십시오. 매크로를 사용하면 --WIN_TYPES.H와 같은 다른 헤더 파일을 사용할 수 있습니다.

typedef ...some struct

--POSIX_TYPES.h

typedef ...some another struct

--program.h

#ifdef WIN32
#define TYPES_H "WINTYPES.H"
#else 
#define TYPES_H "POSIX_TYPES.H"
#endif

#include TYPES_H

내 의견으로는 다른 방식으로 구현하는 것보다 훨씬 읽기 쉽습니다.


2

VA_ARGS는 지금까지 간접적으로 언급 된 것 같습니다.

일반 C ++ 03 코드를 작성할 때 가변 (일반) 매개 변수가 필요한 경우 템플리트 대신 매크로를 사용할 수 있습니다.

#define CALL_RETURN_WRAPPER(FnType, FName, ...)          \
  if( FnType theFunction = get_op_from_name(FName) ) {   \
    return theFunction(__VA_ARGS__);                     \
  } else {                                               \
    throw invalid_function_name(FName);                  \
  }                                                      \
/**/

노트 : 일반적으로 이름 확인 / 투척은 가상으로 통합 될 수도 있습니다.get_op_from_name 기능 . 이것은 단지 예일뿐입니다. VA_ARGS 호출을 둘러싼 다른 일반 코드가있을 수 있습니다.

C ++ 11로 가변 템플릿을 얻은 후에는 템플릿을 사용하여 "적절하게"해결할 수 있습니다.


1

이 트릭은 함수로 에뮬레이트 할 수없는 전처리기를 영리하게 사용한다고 생각합니다.

#define COMMENT COMMENT_SLASH(/)
#define COMMENT_SLASH(s) /##s

#if defined _DEBUG
#define DEBUG_ONLY
#else
#define DEBUG_ONLY COMMENT
#endif

그런 다음 다음과 같이 사용할 수 있습니다.

cout <<"Hello, World!" <<endl;
DEBUG_ONLY cout <<"This is outputed only in debug mode" <<endl;

RELEASE_ONLY 매크로를 정의 할 수도 있습니다.


2
이 트릭은 표준에 따라 작동하지 않습니다. 전처리기를 통해 주석 표식을 만들려고하지만 전처리 기가 실행되기 전에 주석을 제거해야합니다. 적합한 컴파일러는 여기에 구문 오류를 발생시킵니다.
David Thornley

2
죄송하지만 David는 컴파일러에 주석 제거의 두 번째 사본을 포함해야합니다.
Joshua

디버깅 플래그를 전역 const bool로 만들고 다음과 같은 코드를 사용하는 것이 훨씬 쉽습니다. if (debug) cout << "..."; -매크로가 필요 없습니다!
Stefan Monov

@Stefan : 사실, 내가 지금하는 일입니다. 이 경우 디버그가 거짓이면 괜찮은 컴파일러가 코드를 생성하지 않습니다.
Mathieu Pagé

1

또는 옵션을 #define사용하여 컴파일러 명령 행에서 상수를 사용할 수 있습니다 . makefile이 각 플랫폼에 대해 정의 된 상수를 제어 할 수 있기 때문에 여러 플랫폼에 대해 동일한 소프트웨어를 크로스 컴파일 할 때 종종 유용합니다.-D/D

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.