#ifdef vs #if-특정 코드 섹션의 컴파일을 활성화 / 비활성화하는 방법으로 더 나은 / 안전한 방법은 무엇입니까?


112

이것은 스타일의 문제 일 수 있지만, 개발 팀에 약간의 차이가있어서 다른 사람이이 문제에 대해 아이디어가 있는지 궁금했습니다 ...

기본적으로 정상적인 개발 중에 꺼지는 디버그 인쇄 문이 있습니다. 개인적으로 다음을 선호합니다.

//---- SomeSourceFile.cpp ----

#define DEBUG_ENABLED (0)

...

SomeFunction()
{
    int someVariable = 5;

#if(DEBUG_ENABLED)
    printf("Debugging: someVariable == %d", someVariable);
#endif
}

하지만 일부 팀은 다음을 선호합니다.

// #define DEBUG_ENABLED

...

SomeFunction()
{
    int someVariable = 5;

#ifdef DEBUG_ENABLED
    printf("Debugging: someVariable == %d", someVariable);
#endif
}

... 당신에게 더 좋은 방법은 무엇이며 그 이유는 무엇입니까? 내 느낌은 항상 정의 된 것이 있고 다른 정의를 파괴 할 위험이 없기 때문에 첫 번째가 더 안전하다는 것입니다.


참고 : 함께 #if, 당신은 또한 다음 사용할 수 있습니다 #elif와는 달리, 일관된 방식으로 #ifdef. #define BLAH#define BLAH 1#if BLAH
Andrew

답변:


82

#ifdef물론 제 초기 반응은 였지만 #if실제로 이것에 대해 몇 가지 중요한 이점이 있다고 생각합니다 . 그 이유는 다음과 같습니다.

첫째, DEBUG_ENABLED전 처리기 컴파일 된 테스트 에서 사용할 수 있습니다 . 예-종종 디버그가 활성화되었을 때 더 긴 시간 초과를 원하므로을 사용하여 다음 #if과 같이 작성할 수 있습니다.

  DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);

... 대신에 ...

#ifdef DEBUG_MODE
  DoSomethingSlowWithTimeout(5000);
#else
  DoSomethingSlowWithTimeout(1000);
#endif

둘째, #definea에서 전역 상수 로 마이그레이션하려는 경우 더 나은 위치에 있습니다 . #defines는 일반적으로 대부분의 C ++ 프로그래머에게 눈살을 찌푸립니다.

그리고 셋째, 팀에 분열이 있다고 말합니다. 제 생각에는 이것은 다른 회원들이 이미 다른 접근 방식을 채택했으며 표준화해야한다는 것을 의미합니다. 그 판결 #if코드를 사용하는 것을 선호하는 선택 수단되어 #ifdef컴파일 - 그리고 경우에도 실행 -됩니다 DEBUG_ENABLED거짓은. 그리고 그 반대가 아니어야 할 때 생성되는 디버그 출력을 추적하고 제거하는 것이 훨씬 쉽습니다.

아, 그리고 약간의 가독성 포인트. 에서 0/1이 아닌 true / false를 사용할 수 있어야하며 #define값이 단일 어휘 토큰이기 때문에 괄호가 필요하지 않은 한 번입니다.

#define DEBUG_ENABLED true

대신에

#define DEBUG_ENABLED (1)

상수는 디버깅을 활성화 / 비활성화하는 데 사용되지 않을 수 있으므로 #define이 0 인 #ifdef를 트리거하는 것은 그다지 좋지 않을 수 있습니다. 참 / 거짓은 C99에 추가되었으며 C89 / C90에는 존재하지 않습니다.
Michael Carman

마이클 : 그 / 그녀는 옹호 한 에 대해 #ifdef와의 사용을?!
Jon Cage

7
예, 한 가지 문제 #ifdef는 정의되지 않은 것들과 함께 작동한다는 것입니다. 의도적으로 정의되지 않았거나 오타로 인해 정의되지 않았거나 무엇을 가지고 있는지.
bames53

6
답변에 대한 귀하의 추가가 잘못되었습니다. #if DEBUG_ENBALED전처리 기가 감지 한 오류가 아닙니다. 경우 DEBUG_ENBALED정의되지 않은,이 토큰으로 확장 0#if지시.
R .. GitHub의 STOP 돕기 ICE

6
@R .. 많은 컴파일러에서 DEBUG_ENABLED가 정의되지 않은 경우 "#if DEBUG_ENABLED"에 대한 경고를 활성화 할 수 있습니다. GCC에서는 "-Wundef"를 사용합니다. Microsoft Visual Studio에서 "/ w14668"을 사용하여 C4668을 수준 1 경고로 설정합니다.

56

둘 다 끔찍합니다. 대신 다음을 수행하십시오.

#ifdef DEBUG
#define D(x) do { x } while(0)
#else
#define D(x) do { } while(0)
#endif

그런 다음 디버그 코드가 필요할 때마다 D();. 그리고 당신의 프로그램은의 끔찍한 미로로 오염되지 않습니다 #ifdef.


6
@MatthieuM. 사실 원래 버전은 괜찮은 것 같아요. 세미콜론은 빈 문으로 해석됩니다. 그러나 세미콜론을 잊어 버리면 위험 할 수 있습니다.
Casey Kuball 2014 년

31

#ifdef 토큰이 정의되었는지 확인합니다.

#define FOO 0

그때

#ifdef FOO // is true
#if FOO // is false, because it evaluates to "#if 0"

20

우리는 여러 파일에 걸쳐 이와 동일한 문제가 발생했으며 사람들이 "기능 플래그"파일을 포함하는 것을 잊어 버리는 문제가 항상 있습니다 (코드베이스가 41,000 개 이상이면 쉽게 수행 할 수 있음).

feature.h가있는 경우 :

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE 1

#endif // FEATURE_H

하지만 file.cpp에 헤더 파일을 포함하는 것을 잊었습니다.

#if COOL_FEATURE
    // definitely awesome stuff here...
#endif

그러면 문제가 발생합니다. 컴파일러는이 경우 COOL_FEATURE가 정의되지 않은 "거짓"으로 해석하고 코드를 포함하지 못합니다. 예 gcc는 정의되지 않은 매크로에 오류를 발생시키는 플래그를 지원하지만 대부분의 타사 코드는 기능을 정의하거나 정의하지 않으므로 이식성이 떨어집니다.

우리는이 경우를 수정하고 기능의 상태 인 함수 매크로를 테스트하는 이식 가능한 방법을 채택했습니다.

위의 feature.h를 다음과 같이 변경 한 경우 :

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE() 1

#endif // FEATURE_H

하지만 다시 file.cpp에 헤더 파일을 포함하는 것을 잊었습니다.

#if COOL_FEATURE()
    // definitely awseome stuff here...
#endif

정의되지 않은 함수 매크로를 사용하기 때문에 전처리 기가 오류가 발생했을 것입니다.


17

조건부 컴파일을 수행하기 위해 #if와 #ifdef는 거의 동일하지만 완전히 그렇지는 않습니다. 조건부 컴파일이 두 기호에 의존하는 경우 #ifdef도 작동하지 않습니다. 예를 들어 두 개의 조건부 컴파일 기호, PRO_VERSION 및 TRIAL_VERSION이 있다고 가정하면 다음과 같을 수 있습니다.

#if defined(PRO_VERSION) && !defined(TRIAL_VERSION)
...
#else
...
#endif

#ifdef를 사용하면 위의 내용이 훨씬 더 복잡해지며 특히 #else 부분이 작동하게됩니다.

조건부 컴파일을 광범위하게 사용하는 코드를 작업하고 있으며 #if와 #ifdef가 혼합되어 있습니다. 간단한 경우에는 # ifdef / # ifndef를 사용하고 둘 이상의 기호가 평가 될 때마다 #if를 사용하는 경향이 있습니다.


1
#if defined무엇 defined그것은 키 단어 나인가?
nmxprime 2014

15

전적으로 스타일 문제라고 생각합니다. 둘 다 다른 것에 비해 확실한 이점이 없습니다.

일관성이 특정 선택보다 더 중요하므로 팀과 함께 한 가지 스타일을 선택하고이를 고수하는 것이 좋습니다.


8

내가 선호하는 것 :

#if defined(DEBUG_ENABLED)

반대 조건을 찾는 코드를 훨씬 쉽게 발견 할 수있게 해주기 때문에 :

#if !defined(DEBUG_ENABLED)

#ifndef(DEBUG_ENABLED)

8
개인적으로 그 작은 느낌표를 놓치는 것이 더 쉽다고 생각합니다!
Jon Cage

6
구문 강조 기능이 있습니까? :) 구문 강조에서 "ifndef"의 "n"은 모두 같은 색이기 때문에 찾기가 훨씬 더 어렵습니다.
Jim Buck

좋아요, #ifndef가 #if 정의 된 것보다 더 쉽게 찾을 수 있다는 것을 의미했습니다. #if 정의 된 ..과 비교할 때 #if 정의 된 경우 .. 그러나 모든 #if defined / # if! defined 대 # ifdef / # ifndef가 주어지면 둘 다 똑같이 읽을 수 없습니다!
Jon Cage

내가이 댓글 이후 몇 년이 지났 알고 있지만 난 당신이로 쓸 수 지적하고 싶습니다 @JonCage #if ! defined(가) 만들 !미스에 더 눈에 띄는 하드.
Pharap

@Pharap - 그건 확실히 :) 개선과 같은
존 케이지

7

그것은 스타일의 문제입니다. 그러나 더 간결한 방법을 권장합니다.

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print
#endif

debug_print("i=%d\n", i);

이 작업을 한 번 수행 한 다음 항상 debug_print ()를 사용하여 인쇄하거나 아무것도하지 않습니다. (예, 두 경우 모두 컴파일됩니다.) 이렇게하면 코드가 전 처리기 지시문으로 왜곡되지 않습니다.

"표현식이 효과가 없습니다"라는 경고가 표시되고이를 제거하려는 경우 다음과 같은 대안이 있습니다.

void dummy(const char*, ...)
{}

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print dummy
#endif

debug_print("i=%d\n", i);

3
아마도 인쇄 매크로가 최선의 예는 아니었을 것입니다. 우리는 더 표준적인 디버그 코드를 위해 이미 코드베이스에서이 작업을 수행했습니다. 우리는 .. 당신이 여분의 디버깅을 할 수 있습니다 영역의의 #if / #ifdefined 비트를 사용
존 케이지

5

#if스위치가 있음을 감지하면서 기능을 끄려면 0으로 설정하는 옵션을 제공합니다.
개인적으로는 항상 #define DEBUG 1#if 또는 #ifdef로 잡을 수 있습니다.


1
#define DEBUG = 0이 이제 #if를 실행하지 않고 #ifdef를 실행하므로 실패합니다. #ifdef
tloach

1
즉, DEBUG를 완전히 제거하거나 0으로 설정하여 비활성화 할 수 있습니다.
Martin Beckett

이어야합니다 #define DEBUG 1. Not#define DEBUG=1
Keshava GN 2013-04-22

4

#if 및 #define MY_MACRO (0)

#if를 사용하면 "정의"매크로, 즉 "(0)"으로 대체 될 코드에서 검색되는 항목이 생성되었음을 의미합니다. 이것은 잠재적 인 코드 수정으로 코드를 오염시키기 때문에 C ++에서보고 싶지 않은 "매크로 지옥"입니다.

예를 들면 :

#define MY_MACRO (0)

int doSomething(int p_iValue)
{
   return p_iValue + 1 ;
}

int main(int argc, char **argv)
{
   int MY_MACRO = 25 ;
   doSomething(MY_MACRO) ;

   return 0;
}

g ++에서 다음 오류를 제공합니다.

main.cpp|408|error: lvalue required as left operand of assignment|
||=== Build finished: 1 errors, 0 warnings ===|

하나의 오류입니다.

이는 매크로가 C ++ 코드와 성공적으로 상호 작용했음을 의미합니다. 함수 호출이 성공했습니다. 이 간단한 경우에는 재미 있습니다. 그러나 내 코드로 조용히 매크로를 사용하는 경험은 기쁨과 충만 함으로 가득 차 있지 않으므로 ...

#ifdef 및 #define MY_MACRO

#ifdef를 사용한다는 것은 무언가를 "정의"한다는 의미입니다. 가치를 부여하는 것이 아닙니다. 여전히 오염되고 있지만, 적어도 "아무것도 대체하지 않을 것"이며 C ++ 코드에서는 후행 코드 문으로 인식되지 않습니다. 위의 동일한 코드에서 간단한 정의를 사용하면 다음과 같습니다.

#define MY_MACRO

int doSomething(int p_iValue)
{
   return p_iValue + 1 ;
}

int main(int argc, char **argv)
{
   int MY_MACRO = 25 ;
   doSomething(MY_MACRO) ;

   return 0;
}

다음 경고를 제공합니다.

main.cpp||In function int main(int, char**)’:|
main.cpp|406|error: expected unqualified-id before ‘=’ token|
main.cpp|399|error: too few arguments to function int doSomething(int)’|
main.cpp|407|error: at this point in file|
||=== Build finished: 3 errors, 0 warnings ===|

그래서...

결론

내 코드에 매크로없이 살고 싶지만 여러 가지 이유로 (헤더 가드 또는 디버그 매크로 정의) 할 수 없습니다.

그러나 적어도 나는 합법적 인 C ++ 코드로 가능한 최소한의 상호 작용을 만들고 싶습니다. 즉, #ifdef 및 #ifndef (또는 Jim Buck이 제안한대로 정의 된 경우 #ifndef)를 사용하여 값없이 #define을 사용하고, 무엇보다도 이름을 너무 길고 외계인이 사용하지 않을 것입니다. "우연히", 합법적 인 C ++ 코드에는 영향을 미치지 않습니다.

Post Scriptum

이제 내 게시물을 다시 읽을 때 정의에 추가 할 올바른 C ++가 아닐 수있는 값을 찾으려고하면 안되는지 궁금합니다. 같은 것

#define MY_MACRO @@@@@@@@@@@@@@@@@@

#ifdef 및 #ifndef와 함께 사용할 수 있지만 함수 내에서 사용하면 코드가 컴파일되지 않습니다 ... g ++에서 성공적으로 시도했는데 오류가 발생했습니다.

main.cpp|410|error: stray ‘@’ in program|

흥미 롭군. :-)


나는 매크로가 위험 할 수 있다는 데 동의하지만 첫 번째 예제는 디버그하기에 상당히 분명 할 것이며 물론 하나의 오류 만 제공합니다. 왜 더 많은 것을 기대합니까? 매크로의 결과로 훨씬 더 까다로운 오류를 보았습니다 ...
Jon Cage

하나의 솔루션과 다른 솔루션의 차이는 거의 사소한 것이 사실입니다. 하지만이 경우 경쟁하는 두 가지 코딩 스타일에 대해 이야기하고 있으므로 사소한 것조차 무시할 수 없습니다. 그 후에 남은 것은 모두 개인적인 취향이기 때문입니다 (그 시점에서는 정규화해서는 안된다고 생각합니다). )
paercebal

4

그것은 스타일의 문제가 아닙니다. 또한 문제는 안타깝게도 잘못되었습니다. 이러한 전 처리기 지시문을 더 좋거나 더 안전하게 비교할 수는 없습니다.

#ifdef macro

"매크로가 정의 된 경우"또는 "매크로가있는 경우"를 의미합니다. 여기서 매크로의 가치는 중요하지 않습니다. 그것은 무엇이든 될 수 있습니다.

#if macro

항상 값과 비교하면. 위의 예에서는 표준 암시 적 비교입니다.

#if macro !=0

#if 사용 예

#if CFLAG_EDITION == 0
    return EDITION_FREE;
#elif CFLAG_EDITION == 1
    return EDITION_BASIC;
#else
    return EDITION_PRO;
#endif

이제 코드에 CFLAG_EDITION의 정의를 넣을 수 있습니다.

#define CFLAG_EDITION 1 

또는 매크로를 컴파일러 플래그로 설정할 수 있습니다. 또한 여기를 참조하십시오 .


2

첫 번째가 더 명확 해 보입니다. 정의 된 / 정의되지 않은 것에 비해 플래그를 만드는 것이 더 자연스러워 보입니다.


2

둘 다 정확히 동일합니다. 관용적 인 사용에서 #ifdef는 정의 (및 귀하의 예제에서 사용하는 것)를 확인하는 데 사용되는 반면 #if는 #if defined (A) &&! defined (B)와 같은 더 복잡한 표현식에서 사용됩니다.


1
OP는 "#ifdef"와 "#if defined"중 어느 것이 더 나은지 묻는 것이 아니라 "# ifdef / # if defined"와 "#if"사이에서 더 나은 것을 요구했습니다.
shank

1

약간의 OT이지만 전처리기로 로깅을 켜고 끄는 것은 C ++에서 확실히 차선책입니다. 오픈 소스이며 애플리케이션 배포 방법을 제한하지 않는 Apache의 log4cxx 와 같은 멋진 로깅 도구 가 있습니다. 또한 재 컴파일없이 로깅 수준을 변경할 수 있고, 로깅을 끄면 오버 헤드가 매우 낮으며, 프로덕션에서 로깅을 완전히 끌 수있는 기회를 제공합니다.


1
동의, 우리는 실제로 우리의 코드에서, 난 그냥 당신이 등의 #if 사용할 수있는 무언가의 예 싶다고 할
존 케이지

1

를 사용 #ifdef했지만 문서화를 위해 Doxygen으로 전환했을 때 주석 처리 된 매크로를 문서화 할 수 없다는 사실을 발견했습니다 (또는 적어도 Doxygen이 경고를 생성 함). 이는 현재 활성화되지 않은 기능 전환 매크로를 문서화 할 수 없음을 의미합니다.

Doxygen에 대해서만 매크로를 정의 할 수 있지만 이는 코드의 비활성 부분에있는 매크로도 문서화됨을 의미합니다. 개인적으로 기능 스위치를 표시하고 그렇지 않으면 현재 선택된 항목 만 문서화하고 싶습니다. 또한 Doxygen이 파일을 처리 할 때만 정의해야하는 매크로가 많으면 코드가 매우 복잡해집니다.

따라서이 경우 항상 매크로를 정의하고를 사용하는 것이 좋습니다 #if.


0

나는 항상 #ifdef 및 컴파일러 플래그를 사용하여 정의했습니다 ...


(호기심으로) 특별한 이유가 있습니까?
Jon Cage

2
솔직히 나는 그것에 대해 생각해 본 적이 없다. 내가 일한 곳에서 그것이 어떻게 이루어 졌는지. 프로덕션 빌드를 위해 코드를 변경하는 대신 디버그를 위해 'make DEBUG'또는
일반인 경우

0

또는 전역 상수를 선언하고 전 처리기 #if 대신 C ++ if를 사용할 수 있습니다. 컴파일러는 사용하지 않는 분기를 최적화해야하며 코드가 더 깨끗해집니다.

다음은 Stephen C. Dewhurst의 C ++ Gotchas#if 사용에 대해 말하는 것입니다.


1
이것은 형편없는 해결책이며 다음과 같은 문제가 있습니다. 1. 함수에서만 작동하고, 불필요한 클래스 변수 등을 제거 할 수 없습니다. 2. 컴파일러가 도달 할 수없는 코드에 대한 경고를 표시 할 수 있습니다. 3. 여전히 컴파일이 필요한 경우 코드에있는 코드입니다. 모든 디버그 기능을 정의 된 상태로 유지해야합니다.
Don Neufeld

먼저 질문은 특히 debug printfs에 관한 것이기 때문에 불필요한 클래스 변수는 여기서 문제가되지 않습니다. 둘째, 최신 컴파일러의 기능을 고려할 때 #ifdefs를 가능한 한 적게 사용해야합니다. 대부분의 경우 빌드 구성 또는 템플릿 전문화를 대신 사용할 수 있습니다.
Dima

0

드라이버에 조건부 정의를 지정하는 방법이 다른 경우에는 차이가 있습니다.

diff <( echo | g++ -DA= -dM -E - ) <( echo | g++ -DA -dM -E - )

산출:

344c344
< #define A 
---
> #define A 1

즉,의 -DA동의어 -DA=1이며 값을 생략하면 #if A사용시 문제가 발생할 수 있습니다.


0

내가 좋아하는 #define DEBUG_ENABLED (0)디버그 여러 수준의 할 수있는 경우. 예를 들면 :

#define DEBUG_RELEASE (0)
#define DEBUG_ERROR (1)
#define DEBUG_WARN (2)
#define DEBUG_MEM (3)
#ifndef DEBUG_LEVEL
#define DEBUG_LEVEL (DEBUG_RELEASE)
#endif
//...

//now not only
#if (DEBUG_LEVEL)
//...
#endif

//but also
#if (DEBUG_LEVEL >= DEBUG_MEM)
LOG("malloc'd %d bytes at %s:%d\n", size, __FILE__, __LINE__);
#endif

다른 것을 디버깅하는 데 모든 로그 라인을 사용하지 않고도 메모리 누수를 디버그하기가 더 쉽습니다.

또한 #ifndef정의 주변을 통해 명령 줄에서 특정 디버그 수준을 쉽게 선택할 수 있습니다.

make -DDEBUG_LEVEL=2
cmake -DDEBUG_LEVEL=2
etc

이것이 아니라면 #ifdef컴파일러 / 메이크 플래그가 파일의 플래그에 의해 재정의되기 때문에 이점을 줄 것입니다. 따라서 커밋을 수행하기 전에 헤더를 다시 변경하는 것에 대해 걱정할 필요가 없습니다.

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