Search
Duplicate

switch case에서의 명시적 fallthrough

간단소개
fallthrough를 명시적으로 표시하는법을 알아보자
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
C++
Scrap
태그
c++
9 more properties
//오랜만에 써봅니다 수정 보완 지적 언제나 환영합니다!
다음과 같은 코드를 생각해보자
// test.cpp #include <iostream> int main(void) { int c = 2; switch (c) { case (1): std::cout << "case 1" << std::endl; case (2): std::cout << "case 2" << std::endl; case (3): std::cout << "case 3" << std::endl; default: std::cout << "case default" << std::endl; break; } return (0); }
C++
복사
이를 다음과 같이 clang++로 컴파일하고 실행하면 아래와 같이 된다.
❯ clang++ -Wall -Wextra -Werror -std=c++98 test.cpp ❯ ./a.out case 2 case 3 case default
Bash
복사
switch case문은 일치하는 case를 실행한 후 이후 명령어들도 쭉 실행되게 되는데, 이를 fallthrough라고 한다. 따라서 이를 방지하기 위해서는 중간에 break나 return 등으로 빠져나와야한다.
그럼 위 코드를 g++ (gcc의 c++ 컴파일러)로 컴파일해보면 어떻게 될까?
❯ g++ -Wall -Wextra -Werror -std=c++98 test.cpp test.cpp: In function ‘int main()’: test.cpp:8:37: error: this statement may fall through [-Werror=implicit-fallthrough=] 8 | std::cout << "case 1" << std::endl; | ^~~~ test.cpp:9:5: note: here 9 | case (2): | ^~~~ test.cpp:10:37: error: this statement may fall through [-Werror=implicit-fallthrough=] 10 | std::cout << "case 2" << std::endl; | ^~~~ test.cpp:11:5: note: here 11 | case (3): | ^~~~ test.cpp:12:37: error: this statement may fall through [-Werror=implicit-fallthrough=] 12 | std::cout << "case 3" << std::endl; | ^~~~ test.cpp:13:5: note: here 13 | default: | ^~~~~~~ cc1plus: all warnings being treated as errors
Bash
복사
이런식으로 fall through의 가능성이 있다고 implicit-fallthrough 옵션에 의해 warning이 뜨게 된다. (-Wextra 옵션이 -Wimplicit-fallthrough=3 옵션을 포함한다) 해당 fallthrough가 사용자가 의도한 것이 아닌, 실수일 가능성이 있다고 보고 경고해주는 것이다. 이를 통해 clang++과 g++ 등 컴파일러에 따라 띄워주는 경고가 다르다는 것을 알 수 있다. clang++에서는 기본적으로 -Wimplicit-fallthrough 옵션이 포함되지 않으며, clang++ -Wall -Wextra -Werror -Wimplicit-fallthrough 와 같이 명시적으로 넣어서 컴파일해야 경고가 뜬다.
(본 게시글에서는 g++ 11.3 버전을, clang++은 16.0 버전을 이용했다)
해당 경고는 -Wno-implicit-fallthrough를 붙여 컴파일하면 끌 수 있다. 하지만 번거롭다고 경고 옵션을 끄면 정말로 실수하는 경우를 잡지 못할 것이다. 그렇다면 컴파일러에게 실수가 아니라 의도라는 것을 어떻게 알려줄 수 있을까?
크게 2 + 1가지 방법이 있다.
1. 컴파일러 attribute을 이용하는 방법
2. 주석을 이용하는 방법
3. (c++17 부터 가능) [[fallthrough]] 을 이용하는 방법.
gcc 매뉴얼을 찾아보면 내용이 참 긴데, 그중 이런 부분이 있다.
Since there are occasions where a switch case fall through is desirable, GCC provides an attribute, "__attribute__((fallthrough))", that is to be used along with a null statement to suppress this warning that would normally occur
__attribute__((fallthrough)); 를 추가해 명시해줄 수 있다. (위는 gcc 매뉴얼에서가져왔지만, clang++에서도 같은 attribute를 지원하는 듯하다.)
it is also possible to add a fallthrough comment to silence the warning. The whole body of the C or C++ style comment should match the given regular expressions listed below. The option argument n specifies what kind of comments are accepted:
gcc 의 경우 정규표현식을 이용한 주석으로 명시해줄 수도 있다. -Wimplicit-fallthrough=n 의 n 값에 따라 허용하는 형태가 조금씩 다른데, 기본값인 3의 경우 아래와 같다. (0은 경고옵션을 끄는 것과 같고, 4는 더 제한된 형태만을 허용하며, 5는 attribute를 이용한 경우만 허용하고 주석을 이용한 방법은 허용하지 않는다) 이 방법은 clang++에는 적용되지 않는 듯했다.
*<"-fallthrough"> *<"@fallthrough@"> *<"lint -fallthrough[ \t]*"> *<"[ \t.!]*(ELSE,? |INTENTIONAL(LY)? )?FALL(S | |-)?THR(OUGH|U)[ \t.!]*(-[^\n\r]*)?"> *<"[ \t.!]*(Else,? |Intentional(ly)? )?Fall((s | |-)[Tt]|t)hr(ough|u)[ \t.!]*(-[^\n\r]*)?"> *<"[ \t.!]*([Ee]lse,? |[Ii]ntentional(ly)? )?fall(s | |-)?thr(ough|u)[ \t.!]*(-[^\n\r]*)?">
C++
복사
마지막으로 c++11이나 c++14에서는 gnu 확장으로 [[gnu::fallthrough]]; 를 이용할 수 있다고 하며, c++17부터는 c++ 표준으로 [[fallthrough]]; 를 이용할 수 있다. 특정 컴파일러에 종속적인 기능보단 언어 자체의 표준을 이용하는게 좋지 않을까 싶지만, 우리 과제에서는 c++98기능만을 사용할 수 있으므로……ㅠ
#include <iostream> int main(void) { int c = 2; switch (c) { case (1): std::cout << "case 1" << std::endl; __attribute__((fallthrough)); case (2): std::cout << "case 2" << std::endl; // INTENTIONAL FALL-THRU case (3): std::cout << "case 3" << std::endl; // FALLTHROUGH default: std::cout << "case default" << std::endl; break; } return (0); }
C++
복사
위와 같이 써주면 g++에서도 더이상 경고가 뜨지 않게 된다.
참고자료