IT이야기

C++ 강제 컴파일 시간 오류/스위치의 암시적 폴스루에 대한 경고

cyworld 2021. 9. 22. 17:37
반응형

C++ 강제 컴파일 시간 오류/스위치의 암시적 폴스루에 대한 경고


switch 문은 매우 유용할 수 있지만 프로그래머가 break 문을 잊어버린 일반적인 버그로 이어집니다.

switch(val) {
    case 0:
        foo();
        break;
    case 1:
        bar();
        // oops
    case 2:
        baz();
        break;
    default:
        roomba();
}

때때로 fall-through가 명시적으로 요구되기 때문에 분명히 경고를 받지는 않을 것입니다. 좋은 코딩 스타일은 실수가 의도적일 때 주석을 달도록 제안하지만 때로는 충분하지 않습니다.

나는이 질문에 대한 대답은 전혀 없지만, 확신 해요 : 어떤 방법이 현재 존재 (또는 미래에 제안 된) 오류가 발생하도록 컴파일러에 요청할 수 있도록 당신이 경우에 (적어도 경고 나!) case하지 않습니다 break;의 효과에 대해 적어도 하나 또는 무언가가 // fallthru있습니까? switch명령문 을 사용하기 위한 방어적인 프로그래밍 옵션이 있으면 좋을 것 입니다.


잘 clang에는 -Wimplicit-fallthrough내가 알지 못했지만 사용하여 찾은 것이 -Weverything있습니다. 따라서 이 코드의 경우 다음과 같은 경고가 표시됩니다( 실시간 참조 ).

warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough]
case 2:
^
note: insert '[[clang::fallthrough]];' to silence this warning
case 2:
^
[[clang::fallthrough]]; 
note: insert 'break;' to avoid fall-through
case 2:
^
break; 

이 플래그에 대해 찾을 수 있는 유일한 문서는 다음과 같은 속성 참조 에 있습니다.

clang::fallthrough 속성은 -Wimplicit-fallthrough 인수와 함께 사용되어 스위치 레이블 사이에 의도적인 폴스루에 주석을 답니다. 명령문과 다음 스위치 레이블 사이의 실행 지점에 있는 null 명령문에만 적용할 수 있습니다. 이러한 위치를 특정 주석으로 표시하는 것이 일반적이지만 이 속성은 주석을 컴파일러에서 확인할 수 있는 보다 엄격한 주석으로 대체하기 위한 것입니다.

명시적 폴스루를 표시하는 방법의 예를 제공합니다.

case 44:  // warning: unannotated fall-through
g();
[[clang::fallthrough]];
case 55:  // no warning

명시적 폴스루를 표시하기 위해 속성사용하면 이식성이 없다는 단점이 있습니다. Visual Studio오류를 gcc생성하고 다음 경고를 생성합니다.

warning: attributes at the beginning of statement are ignored [-Wattributes]

사용하려는 경우 문제 -Werror입니다.

나는 이것을 시도했고이 경고를 지원하지 않는 gcc 4.9것 같습니다 gcc.

오류: 인식할 수 없는 명령줄 옵션 '-Wimplicit-fallthrough'

현재 GCC 7 , -Wimplicit-fallthrough지원되고 __attribute__((fallthrough))위해 fallthrough 의도적 때 경고를 억제하는데 사용될 수있다. GCC는 특정 시나리오에서 "fallthrough" 주석을 인식하지만 상당히 쉽게 혼동될 수 있습니다 .

에 대해 그러한 경고를 생성하는 방법이 없습니다 Visual Studio.

참고로 Chandler Carruth-Weverything프로덕션 용도가 아니라고 설명합니다 .

이것은 말 그대로 Clang의 모든 경고를 활성화하는 미친 그룹입니다. 코드에서 이것을 사용하지 마십시오. Clang 개발자 또는 어떤 경고가 있는지 탐색하기 위한 것입니다.

그러나 어떤 경고가 존재하는지 알아내는 데 유용합니다.

C++17 변경 사항

C++17 에서는 [dcl.attr.fallthrough]p1에 포함된 [ [fallthrough]] 속성을 얻습니다 .

속성 토큰 폴스루는 null 문(9.2)에 적용될 수 있습니다. 그러한 진술은 대체 진술입니다. 속성 토큰 폴스루는 각 속성 목록에 최대 한 번 나타나야 하며 속성 인수 절은 없어야 합니다. fallthrough 문은 둘러싸는 switch 문(9.4.2) 내에서만 나타날 수 있습니다. fallthrough 문 다음에 실행될 다음 문은 레이블이 동일한 switch 문에 대한 case 레이블 또는 기본 레이블인 레이블이 지정된 문이어야 합니다. 그러한 진술이 없으면 프로그램이 잘못된 것입니다.

...

[ Example:
void f(int n) {
void g(), h(), i();
switch (n) {
  case 1:
  case 2:
    g();
    [[fallthrough]];
  case 3: // warning on fallthrough discouraged
    h();
  case 4: // implementation may warn on fallthrough
    i();
    [[fallthrough]]; // ill-formed
  }
}
—end example ]

attribute 를 사용하는 라이브 예제를 참조하십시오 .


나는 항상 다음과 같이 break;각각 앞에 씁니다 case.

switch(val) {
    break; case 0:
        foo();
    break; case 1:
        bar();
    break; case 2:
        baz();
    break; default:
        roomba();
}

이렇게 하면 break;누락된 경우 훨씬 더 명확해 집니다. 이니셜 break;은 중복된다고 생각하지만 일관성을 유지하는 데 도움이 됩니다.

이것은 종래 switch나는 단순히 후 보통 인 줄 바꿈 제거, 다른 방법으로 공백을 사용했습니다, 문 break;및 다음 이전을 case.


조언: case 절 사이에 지속적으로 빈 줄을 넣으면 코드를 훑어보는 사람에게 '중단'이 없는 것이 더 잘 보입니다.

switch (val) {
    case 0:
        foo();
        break;

    case 1:
        bar();

    case 2:
        baz();
        break;

    default:
        roomba();
}

개별 케이스 절 안에 많은 코드가 있을 때는 효과적이지 않지만, 그 자체로 나쁜 코드 냄새가 나는 경향이 있습니다.


강박증에 대한 답변은 다음과 같습니다.

First, switch statements are fancy gotos. They can be combined with other control flow (famously, Duff's Device), but the obvious analogy here is a goto or two. Here's a useless example:

switch (var) {
    CASE1: case 1:
        if (foo) goto END; //same as break
        goto CASE2; //same as fallthrough
    CASE2: case 2:
        break;
    CASE3: case 3:
        goto CASE2; //fall *up*
    CASE4: case 4:
        return; //no break, but also no fallthrough!
    DEFAULT: default:
        continue; //similar, if you're in a loop
}
END:

Do I recommend this? No. In fact, if you're considering this just to annotate a fall-through, then your problem is actually something else.

This sort of code does make it very clear that a fall-through can happen in case 1, but as the other bits show, this is a very powerful technique in general that also lends itself to abuse. Be careful if you use it.

Forgetting a break? Well, then you'll also occasionally forget whatever annotation you pick. Forgetting to account for fall-through when changing a switch statement? You're a bad programmer. When modifying switch statements(or really any code), you need to first understand them.


Honestly, I very seldom make this kind of error (forgetting a break)--certainly less than I made other "common" programming errors (strict aliasing, for example). To be safe, I currently do (and recommend you to do) just write //fallthrough, since this at least clarifies intention.

Other than that, it's a just a reality that programmers need to accept. Proofread your code after you write it and find the occasional problem with debugging. That's life.

ReferenceURL : https://stackoverflow.com/questions/27965722/c-force-compile-time-error-warning-on-implicit-fall-through-in-switch

반응형