#ifdef vs #if- 특정 코드 섹션의 컴파일을 활성화/비활성화하는 방법으로 더 나은/더 안전합니까?
-
02-07-2019 - |
문제
이것은 스타일의 문제 일지 모르지만 우리의 개발자 팀에는 약간의 분열이 있으며 다른 사람이 그 문제에 대한 아이디어가 있는지 궁금했습니다 ...
기본적으로 정상적인 개발 중에 꺼지는 디버그 인쇄문이 있습니다. 개인적으로 나는 다음을 선호합니다.
//---- 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
}
... 그 중 어떤 방법이 당신에게 더 잘 들리며 그 이유는 무엇입니까? 내 느낌은 항상 정의 된 것이 있고 다른 곳에서 다른 정의를 파괴 할 수있는 위험이 없기 때문에 첫 번째는 더 안전하다는 것입니다.
해결책
나의 초기 반응은했다 #ifdef
, 물론이야, 하지만 나는 생각해 #if
실제로 이것에 대한 몇 가지 중요한 이점이 있습니다. 이유는 다음과 같습니다.
먼저 사용할 수 있습니다 DEBUG_ENABLED
전처리에서 그리고 컴파일 된 테스트. 예 - 종종 디버그가 활성화 될 때 더 긴 시간 초과를 원하므로 사용하십시오. #if
, 나는 이것을 쓸 수있다
DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);
... 대신에 ...
#ifdef DEBUG_MODE
DoSomethingSlowWithTimeout(5000);
#else
DoSomethingSlowWithTimeout(1000);
#endif
둘째, 당신은 당신이 #define
글로벌 상수에. #define
S는 일반적으로 대부분의 C ++ 프로그래머에 의해 눈살을 찌푸립니다.
그리고 셋째, 당신은 당신이 당신의 팀을 분할한다고 말합니다. 내 생각에 이것은 다른 회원들이 이미 다른 접근법을 채택했으며 표준화해야한다는 것을 의미합니다. 판결 #if
선호하는 선택은 해당 코드를 사용하는 것을 의미합니다 #ifdef
언제라도 컴파일됩니다 DEBUG_ENABLED
거짓입니다. 그리고 그것은입니다 많이 추적하고 그 반대가 아닌 경우 생성되는 디버그 출력을 쉽게 제거하고 제거 할 수 있습니다.
아, 그리고 작은 가독성 지점. 0/1 대신 True/False를 사용할 수 있어야합니다. #define
, 값은 단일 어휘 토큰이기 때문에 주변의 괄호가 필요하지 않습니다.
#define DEBUG_ENABLED true
대신에
#define DEBUG_ENABLED (1)
다른 팁
둘 다 끔찍합니다. 대신, 이것을 수행하십시오 :
#ifdef DEBUG
#define D(x) do { x } while(0)
#else
#define D(x) do { } while(0)
#endif
그런 다음 디버그 코드가 필요할 때마다 안에 넣으십시오. D();
. 그리고 당신의 프로그램은 끔찍한 미로로 오염되지 않습니다 #ifdef
.
#ifdef
주어진 토큰이 정의되어 있는지 확인하십시오
#define FOO 0
그 다음에
#ifdef FOO // is true
#if FOO // is false, because it evaluates to "#if 0"
우리는 여러 파일에서 동일한 문제를 겪었으며 사람들이 "피처 플래그"파일을 포함하는 것을 잊어 버리는 사람들에게는 항상 문제가 있습니다 (코드베이스는> 41,000 파일이 쉽습니다).
기능이 있다면 H :
#ifndef FEATURE_H
#define FEATURE_H
// turn on cool new feature
#define COOL_FEATURE 1
#endif // FEATURE_H
그러나 파일에 헤더 파일을 포함시키는 것을 잊었습니다.
#if COOL_FEATURE
// definitely awesome stuff here...
#endif
그런 다음 문제가 발생하면 컴파일러는이 경우 Cool_Feature가 "False"로 정의되지 않은 것으로 해석하고 코드를 포함하지 않습니다. 예 GCC는 정의되지 않은 매크로에 오류를 일으키는 플래그를 지원하지만 대부분의 타사 코드는 기능을 정의하거나 정의하지 않으므로 휴대용이 아닙니다.
우리는이 케이스에 대한 휴대용 방법을 채택하고 기능 매크로 기능을 테스트했습니다.
위의 기능을 변경 한 경우 :
#ifndef FEATURE_H
#define FEATURE_H
// turn on cool new feature
#define COOL_FEATURE() 1
#endif // FEATURE_H
그러나 다시 파일에 헤더 파일을 포함시키는 것을 잊었습니다.
#if COOL_FEATURE()
// definitely awseome stuff here...
#endif
정의되지 않은 함수 매크로의 사용으로 인해 사전 처리기가 오류가 발생했을 것입니다.
조건부 편집을 수행하기 위해 #if 및 #ifdef가 거의 동일하지만 그다지 그렇지는 않습니다. 조건부 편집이 두 기호에 의존하는 경우 #ifdef도 작동하지 않습니다. 예를 들어, 두 가지 조건부 컴파일 기호, pro_version 및 river_version이 있다고 가정하면 다음과 같은 것이있을 수 있습니다.
#if defined(PRO_VERSION) && !defined(TRIAL_VERSION)
...
#else
...
#endif
#ifdef를 사용하면 위의 것이 훨씬 더 복잡해지며, 특히 #ELSE 부분이 작동하도록합니다.
조건부 컴파일을 광범위하게 사용하는 코드에서 작업하고 있으며 #IF & #ifdef가 혼합되어 있습니다. 우리는 간단한 경우에 #ifdef/ #ifndef를 사용하는 경향이 있으며 두 개 이상의 기호가 평가 될 때마다 #if #.
나는 그것이 전적으로 스타일의 문제라고 생각합니다. 다른 사람보다 명백한 이점도 없습니다.
일관성은 특정 선택보다 더 중요하므로 팀과 함께 하나의 스타일을 선택하고이를 고수하는 것이 좋습니다.
나는 나 자신을 선호한다 :
#if defined(DEBUG_ENABLED)
반대 조건을 찾는 코드를 쉽게 만들 수있게되므로 훨씬 쉽게 찾을 수 있습니다.
#if !defined(DEBUG_ENABLED)
vs.
#ifndef(DEBUG_ENABLED)
스타일의 문제입니다. 그러나 더 간결한 방법을 추천합니다.
#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);
#if
스위치가 있음을 감지하면서 기능을 끄기 위해 0으로 설정하는 옵션을 제공합니다.
개인적으로 나는 항상 #define DEBUG 1
그래서 나는 #if 또는 #ifdef로 그것을 잡을 수 있습니다
#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 ++ 코드는 Lagitimate Code 문으로 보이지 않습니다. 간단한 정의와 함께 위의 동일한 코드는 다음과 같습니다.
#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 ++ 코드로 대화식이 가장 적을 수있게하고 싶습니다. 이는 값없이 #define을 사용하고 #ifdef 및 #ifndef (또는 Jim Buck이 제안한대로 정의 된 #if)를 사용하는 것을 의미하며, 무엇보다도 이름을 너무 길고 외계인을 사용하지 않습니다. 그것은 "우연히", 그리고 그것은 결코 합법적 인 C ++ 코드에 영향을 미치지 않을 것입니다.
포스트 Scriptum
이제 내 게시물을 다시 읽을 때, 내 정의에 추가 할 C ++가 맞지 않는 값을 찾지 않아야하는지 궁금합니다. 같은 것
#define MY_MACRO @@@@@@@@@@@@@@@@@@
#ifdef 및 #ifndef와 함께 사용할 수는 있지만 함수 내에서 사용하면 코드 컴파일을 작성하지 못하게합니다 ... G ++에서 성공적으로 시도해 보았습니다. 오류가 발생했습니다.
main.cpp|410|error: stray ‘@’ in program|
흥미로운. :-)
첫 번째는 나에게 더 명확 해 보인다. 정의/정의되지 않은 것과 비교하여 더 자연스럽게 보이는 것처럼 보입니다.
둘 다 정확히 동일합니다. 관용적으로 사용하면 #ifdef는 정의 성 (및 예제에서 사용하는 내용)을 확인하는 데 사용되는 반면 #if는 #if 정의 (a) && defined (b)와 같은보다 복잡한 표현식에 사용됩니다.
약간의 구약이지만 전처리기로 로깅을 켜거나 끄는 것은 C ++에서 분명히 차선책입니다. Apache 's와 같은 멋진 로깅 도구가 있습니다 log4cxx 오픈 소스이며 응용 프로그램을 배포하는 방법을 제한하지 않습니다. 또한 재 컴파일없이 로깅 레벨을 변경하고 로깅을 끄면 오버 헤드가 매우 낮으며 생산에서 완전히 로그를 끄는 기회를 제공합니다.
그것은 전혀 스타일의 문제가 아닙니다. 또한 질문은 불행히도 잘못입니다. 더 나은 또는 더 안전한 의미 에서이 전처리 서기 지시문을 비교할 수 없습니다.
#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
또는 매크로를 컴파일러 플래그로 설정할 수 있습니다. 또한 여기를 봐.
나는 사용했었다 #ifdef
, 그러나 문서화를 위해 Doxygen으로 전환했을 때, 주석은 매크로를 문서화 할 수 없다는 것을 알았습니다 (또는 적어도 Doxygen은 경고를 생성합니다). 즉, 현재 활성화되지 않은 기능 스위치 매크로를 문서화 할 수 없습니다.
Doxygen에 대해서만 매크로를 정의 할 수는 있지만, 이는 코드의 비활성 부분의 매크로도 문서화 될 것임을 의미합니다. 개인적으로 기능 스위치를 표시하고 그렇지 않으면 현재 선택된 내용 만 문서화하고 싶습니다. 또한 Doxygen이 파일을 처리 할 때만 정의 해야하는 많은 매크로가 있으면 코드가 매우 지저분 해집니다.
따라서이 경우 항상 매크로를 정의하고 사용하는 것이 좋습니다. #if
.
나는 항상 #ifdef 및 컴파일러 플래그를 사용하여 정의했습니다 ...
또는 전역 상수를 선언하고 전처리 기 #IF 대신 C ++를 사용할 수 있습니다. 컴파일러는 사용하지 않은 분기를 최적화해야하며 코드는 더 깨끗합니다.
여기에 무엇이 있습니다 C ++ gotchas Stephen C. Dewhurst는 #if 's를 사용하는 것에 대해 말합니다.
운전자에게 조건부 정의를 지정하는 다른 방법의 경우 차이가 있습니다.
diff <( echo | g++ -DA= -dM -E - ) <( echo | g++ -DA -dM -E - )
산출:
344c344
< #define A
---
> #define A 1
이것은 그것을 의미합니다 -DA
동의어입니다 -DA=1
그리고 가치가 생략되면 #if A
용법.