//오랜만에 써봅니다 수정 보완 지적 언제나 환영합니다!
다음과 같은 코드를 생각해보자
// 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++에서도 더이상 경고가 뜨지 않게 된다.
참고자료