C/C++: do-while(0)을 사용하는 방법; C4127과 같은 컴파일러 경고 없이 구성?
저는 이 답변에 설명된 이유로 #defines에서 do-while(0) construct를 자주 사용합니다.또한 저는 더 많은 잠재적인 문제를 포착하고 제 코드를 더 강력하고 교차 플랫폼으로 만들기 위해 컴파일러의 가능한 높은 경고 수준을 사용하려고 노력하고 있습니다.그래서 저는 일반적으로-Wall 및 gcc 함께와 /WallMSVC와 함께.
불행하게도 MSVC는 do-while(0) 구조에 대해 불평합니다.
foo.c(36) : warning C4127: conditional expression is constant
이 경고를 어떻게 해야 합니까?
모든 파일에 대해 전체적으로 사용하지 않도록 설정하시겠습니까?그것은 저에게 좋은 생각이 아닌 것 같습니다.
요약:.이 경우 이 경고(C4127)는 미묘한 컴파일러 버그입니다.사용하지 않도록 설정하십시오.
상세:
표현이 않은 : 적표명현않백예상상평은황상수위것포니다습한이었착기하황을가는되로서에지하이논리예▁it▁as▁in,()에서 될 때 을 포착하기 이었습니다.if(a==a && a!=a)그리고 어떻게든, 그것은 변했습니다.while(true)그리고 다른 유용한 구문이 무효화됩니다.
는 마크로권방로법을 사용할 것을 합니다.for(;;)무한 루프의 경우 이 경고를 설정하고 사용자의 경우에 대한 해결책이 없습니다.이것은 우리 회사의 개발 규약이 사용하지 않도록 허용하는 몇 안 되는 레벨 4 경고 중 하나입니다.
아마도 당신의 코드는 부엉이가 더 필요할 것입니다.
do { stuff(); } while (0,0)
또는 사진이 덜 발생하지만 경고가 덜 발생하는 경우:
do { stuff(); } while ((void)0,0)
Michael Burr가 Carl Smotricz의 답변에서 언급했듯이 Visual Studio 2008+의 경우 __pragma를 사용할 수 있습니다.
#define MYMACRO(f,g) \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
do { f; g; } while (0) \
__pragma(warning(pop))
줄에 수 요. (" 당은그한놓수줄있다니습을에것을신▁the다" 없이)\매크로를 읽을 수 없도록 하려면 이 옵션을 선택합니다.
저는 여기 답변을 바탕으로 한 패턴이 있으며, 이 패턴은 clang, gcc 및 MSVC에서 작동합니다.다른 사람들에게 유용하기를 바라며 그리고 여기에 있는 답변들이 제가 그것을 공식화하는 데 도움이 되었기 때문에 저는 그것을 여기에 올립니다.
#ifdef WIN32
# define ONCE __pragma( warning(push) ) \
__pragma( warning(disable:4127) ) \
while( 0 ) \
__pragma( warning(pop) )
#else
# define ONCE while( 0 )
#endif
그리고 저는 이렇게 사용합니다.
do {
// Some stuff
} ONCE;
매크로에서도 사용할 수 있습니다.
void SomeLogImpl( const char* filename, int line, ... );
#ifdef NDEBUG
# define LOG( ... )
#else
# define LOG( ... ) do { \
SomeLogImpl( __FILE__, __LINE__, __VA_ARGS__ ); \
} ONCE
#endif
F가 함수에서 'ONCE'를 사용하는 경우 위에서 언급한 경우에도 이 기능이 작동합니다.
#define F( x ) do { f(x); } ONCE
...
if (a==b) F(bar); else someFunc();
편집: 몇 년 후, 저는 제가 실제로 이 매크로를 작성한 패턴, 즉 "스위치처럼 고토" 패턴을 추가하는 것을 잊었다는 것을 깨달았습니다.
do {
begin_some_operation();
if( something_is_wrong ) {
break;
}
continue_big_operation();
if( another_failure_cond ) {
break;
}
finish_big_operation();
return SUCCESS;
} ONCE;
cleanup_the_mess();
return FAILURE;
이렇게 하면 정리 및 반환 코드로 이동하는 것보다 더 체계적인 시도/마침표 구성을 제공합니다.(0) 동안 대신 이 ONCE 매크로를 사용하면 VS가 종료됩니다.
최신 버전의 MS 컴파일러를 사용하면 경고 억제를 사용할 수 있습니다.
#define MY_MACRO(stuff) \
do { \
stuff \
__pragma(warning(suppress:4127)) \
} while(0)
푸시/비활성화/팝업도 가능하지만 억제하는 것이 훨씬 편리합니다.
이 컴파일러 버그는 Visual Studio 2015 업데이트 1에서 수정되었습니다. 릴리스 노트에서 언급하지 않아도 됩니다.
이 버그는 이전 답변 중 하나에서 설명되었습니다.
요약:.이 경우 이 경고(C4127)는 미묘한 컴파일러 버그입니다.사용하지 않도록 설정하십시오.
이는 논리적 표현식이 비정상적인 상황(예: if(a==a&a!=a)에서 상수로 평가되는 상황을 포착하기 위한 것으로, 어떻게든 (참) 및 기타 유용한 구문이 무효화되는 동안에 변환되었습니다.
다음은 C4127, C4548 및 C6319(VS2013 코드 분석 경고)를 피하고 매크로나 플러그마를 필요로 하지 않는 다른 가능한 접근 방식입니다.
static const struct {
inline operator bool() const { return false; }
} false_value;
do {
// ...
} while (false_value);
이렇게 하면 GCC 4.9.2 및 VS2013에서 경고 없이 최적화되고 컴파일됩니다.실제로는 네임스페이스에 들어갈 수 있습니다.
이 경고는 다음으로 인해 발생합니다.while(false)이 사이트는 이 문제를 해결하는 방법의 예를 제공합니다.사이트의 예(코드에 맞게 다시 작업해야 함):
#define MULTI_LINE_MACRO_BEGIN do {
#define MULTI_LINE_MACRO_END \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
} while(0) \
__pragma(warning(pop))
#define MULTI_LINE_MACRO \
MULTI_LINE_MACRO_BEGIN \
std::printf("Hello "); \
std::printf("world!\n"); \
MULTI_LINE_MACRO_END
BEGIN과 END 사이에 코드를 삽입하기만 하면 됩니다.
사용할 수 있습니다.
do {
// Anything you like
} WHILE_FALSE;
그리고 앞에서 정의합니다.WHILE_FALSE매크로는 다음과 같습니다.
#define WHILE_FALSE \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
while(false) \
__pragma(warning(pop))
MSVC++2013에서 확인됨.
이 "while (0)" 물건은 해킹이고 당신을 물어뜯기 위해 돌아섰습니다.
당신의 컴파일러는 다음을 제공합니까?#pragma특정 오류 메시지를 선택적으로 로컬로 끄기 위한 s?만약 그렇다면, 그것은 합리적인 대안일 수도 있습니다.
#define STUFF for (bool b = true; b;) do {f(); g(); b = false;} while (b)?
#define STUFF for (;;) {f(); g(); break;}?
식에 사용할 다중 문 매크로에 대해 do-while(0) 구문 대신 쉼표 연산자를 사용할 수 있습니다.그래서 대신에:
#define FOO(...) do { Statement1; Statement2; Statement3; } while(0)
사용:
#define FOO(...) (Statement1, Statement2, Statement3)
이 기능은 플랫폼과 독립적으로 작동하며 컴파일러 경고를 방지할 수 있습니다(가장 높은 경고 수준이 선택된 경우에도).매크로가 포함된 쉼표(두 번째 FOO)에서는 마지막 문(Statement3)의 결과가 전체 매크로의 결과가 됩니다.
저는 이것이 가장 짧은 버전이라는 것을 알았습니다.
do {
// ...
}
while (([]() { return 0; })()) /* workaround for MSVC warning C4172 : conditional expression is constant */
컴파일러에 의해 최적화되었는지 확인하지 않았지만, 그렇다고 생각합니다.
사용할 수 있습니다.for루프 형식:
for (;;) {
// code
break;
}
매크로:
#define BEGIN \
for (;;) {
#define END \
break; }
저는 그 일로 신경 쓴 적이 없다고 말해야겠습니다.매크로로 구성하는 동안.매크로의 모든 코드는 그 자체로 중괄호에 포함되지만, do-..가 없으면..반면에. 예를 들면:
#define F(x) \
{ \
x++; \
} \
int main() {
int a = 1;
F(a);
printf( "%d\n", a );
}
또한 저만의 코딩 표준(그리고 수년간 비공식적인 관행)은 발생하는 모든 블록을 중괄호로 둘러싸도록 하는 것이었고, 이는 또한 문제를 거의 제거합니다.
해결책이 있지만 코드에 주기를 더 추가합니다.while 조건에서 명시적 값을 사용하지 마십시오.
다음과 같이 만들 수 있습니다.
file1.h
extern const int I_am_a_zero;
#define MY_MACRO(foo,bar) \
do \
{ \
} \
while(I_am_a_zero);
변수 I_am_a_zero는 일부 .c 파일에 정의되어야 합니다.
어쨌든 이 경고는 GCC에 나타나지 않습니다 :)
이 관련 질문을 참조하십시오.
#pragma warning을 사용하여 다음 작업을 수행할 수 있습니다.
- 국가를 구함
- 경고를 해제합니다.
- 위반 코드를 작성합니다.
- 경고를 이전 상태로 되돌립니다.
(프래그마스 전에 #가 필요하지만 SO는 그들을 처리하는 동시에 포맷하는 데 어려움을 겪고 있습니다.)
#pragma warning( push )
#pragma warning( disable: 4127 )
// Your code
#pragma warning( pop )
경고를 설정/해제하기 위해 선택할 수 있는 명령줄 인수를 방해하지 않기 때문에 경고를 사용하지 않고 경고를 푸시/팝업할 수 있습니다(다른 사용자가 명령줄을 사용하여 경고를 해제할 수도 있습니다. 강제로 다시 설정하지 않으려면...).위의 코드는 그것을 다룹니다).
원하는 부품에 대해서만 경고를 제어할 수 있으므로 경고를 전체적으로 해제하는 것보다 좋습니다.또한 매크로의 일부로 만들 수도 있습니다.
다음은 C4127 경고 없이 작동합니다.
#define ALWAYS_TRUE(zzsome) ((##zzsome)==(##zzsome))
void foo()
{
int a = 0;
while( ALWAYS_TRUE(a) )
{
}
}
물론 컴파일러는 똑똑하며 zzome이 상수가 되어서는 안 됩니다.
이렇게 하면 경고가 비활성화되고 컴파일러는 여전히 코드를 최적화할 수 있습니다.
static inline bool to_bool(const bool v) { return v; }
if (to_bool(0)) { // no warning here
dead_code(); // will be compiled out (by most compilers)
}
do { something(); } while(to_bool(0)); // no extra code generated
제가 쓰겠습니다.
for(int i = 0; i < 1; ++i) //do once
{
}
이는 다음과 같습니다.
do
{
}while(0);
경고를 표시하지 않습니다.
프로젝트에서 이 경고를 사용하지 않으려면 컴파일러 스위치 /wd"4127"을 사용하십시오.
언급URL : https://stackoverflow.com/questions/1946445/c-c-how-to-use-the-do-while0-construct-without-compiler-warnings-like-c412
'programing' 카테고리의 다른 글
| 로컬 XML 파일에서 테이블로 데이터를 로드할 때 MariaDB node.js 스크립트에서 ER_LOCAL_INFILE_WRONG_FILENAME 오류가 발생함 (0) | 2023.06.18 |
|---|---|
| django.db.migrations.exceptions.일관성 없는 마이그레이션역사 (0) | 2023.06.18 |
| Oracle 10에서 잠금 제거 (0) | 2023.06.13 |
| SQLAlchemy에서 SQL 쿼리 인쇄를 예쁘게 포맷하는 방법 (0) | 2023.06.13 |
| RVM 설치 중 "gpg: command not found" 오류를 해결하는 방법은 무엇입니까? (0) | 2023.06.13 |