Unused parameter 경고 잠재우기

Unused parameter warning

더 안전하게 C++ 코드를 작성하는법 글에 나왔던 것 처럼 -Wall 플래그를 사용해서 빌드를 할 때, 함수 인자로 받은 변수를 함수에서 사용하지 않을 경우 경고가 나타나게 됩니다.

1
2
3
4
5
6
In file included from /home/hyunggi/c++/directory/sample_code.cpp:10:
/hyunggi/c++/directory/sample_code.hpp: In member function ‘virtual bool someClass::someNamespace::someFunction(const std::string&)’:
/hyunggi/c++/directory/sample_code.hpp:30:53: error: unused parameter ‘slam_is_awesome’ [-Werror=unused-parameter]
30 | virtual bool someFunction(const std::string& slam_is_awesome)
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
cc1plus: all warnings being treated as errors

 

이 경고의 의미는 아래와 같습니다.

함수에서 쓰겠다고 인자를 받았는데, 왜 안써? 너 이거 쓰는거 까먹은거 아냐?? 변수명 잘못쓴거 아냐??? 아니 그럴꺼면 인자로 왜 받은거야????

 

인자로 받은건 꼭 써줘야합니다. 그게 안전한 프로그램입니다.

하지만 레거시 코드를 리팩토링 할 때는 막 함수 몇백개에서 이러한 경고가 나타나는 경우가 있습니다.

함수 하나씩 리팩토링을 하며 해결해나가고 싶은데, -Werror 플래그 때문에 빌드 자체가 안되기도 합니다.

-Werror를 끄고 싶은 마음도 들지만, 그러면 수많은 warning이 있을 때 진짜 크리티컬한 warning을 못보게 되지 않을까 걱정이 됩니다.

이번 글에서는 이런 unused parameter warning을 잠시동안 해제하는 방법에 대해 알아봅니다.

 

TLDR;

  • C++17 이상이라면 [[maybe_unused]] 씁시다
  • C++17보다 낮은 스탠다드를 쓰지만 Boost 프레임워크를 쓴다면 boost::ignore_unused()를 씁시다
  • 왠만하면 처음부터 잘 짭시다
  • 여의치 않는다면 std::ignore를 써서 표시해두고, 빠르게 리팩토링해서 경고가 안나게 합시다
  • 경고는 절대 끄지 맙시다 =.=

 


쓰면 안되는 방법들

우선 쓰지 말아야 할 방법들에 대해 먼저 알아봅시다.

 

-Wno-unused-parameter 플래그 쓰지마세요

컴파일러 경고 자체를 꺼버리는 방법입니다.

감히 제가 뭔데 컴파일러를 무시할까요… 컴파일러 경고는 끄는게 아닙니다.

절대 쓰지 맙시다.

 

void cast 하지마세요

아래 방법과 같은 방식으로, void로 사용하지 않은 변수를 캐스팅하는 겁니다.

void casting은 전혀 아무런 의미도 가지지 않지만, 적어도 변수에 어떠한 액션을 취했기 때문에 컴파일러 경고가 사라지게 됩니다.

1
2
3
4
5
6
T foo(T var1, T var2, T unused_parameter)
{
(void)(unused_parameter); // 또는 static_cast<void>(unused_parameter);
return var1 + var2;
}

근데 아니 아무 죄도 없는 변수는 왜 공허로 보냅니까?

void casting의 목적은 컴파일러 경고를 끄기 위함이 아닙니다.

그렇다고 주석으로 ‘컴파일러 경고를 끄기 위해 사용함’ 이라고 적는건 더 이상합니다.

이럴거면 차라리 함수를 제대로 리팩토링 해줍시다.

 

ignore 함수나 매크로 지정 하지마세요

1
2
3
4
5
6
7
8
9
10
11
12
#define UNUSED(x) (void)(x)

template <typename T>
void ignore_unused(T &&)
{ }

T foo(T var1, T var2, T unused_parameter)
{
UNUSED(unused_parameter); // 매크로 방법
ignore_unused(unused_parameter); // 함수 방법
return var1 + var2;
}

void cast 방식보다는 낫지만, C++ 에서 이런 매크로 사용하지 맙시다.

매크로는 C++ 코드가 C 코드 처럼 보이게 만듭니다.

함수로 만들면 그래도 가독성은 좀더 좋아지지만… 똑같습니다.

 


적당히 써줄만한 방법들

그래도 봐줄만한 방법들을 소개합니다.

 

attribute((unused))

gcc/clang에서 정의한 ‘사용하지 않는 인자’ attribute를 사용하는 방법입니다.

gcc와 clang에서 사용하는건 저희도 써도 괜찮지 않을까 생각합니다.

단점이라면 함수 인자 리스트가 좀 길어집니다.

하지만 적당히 정상적으로 코드를 짰다면 (i.e. unused parameter가 2개 이상이 아니라면), 함수 인자 리스트가 그렇게 길어질리가 없습니다.

Unused parameter가 2개 이상이라면, 그냥 함수 자체를 다시 짜시죠…

1
2
3
4
T foo(T var1, T var2, __attribute__((unused)) T unused_parameter)
{
return var1 + var2;
}

 

변수 주석처리

좀 애매한 방법이지만 작동하는 방법입니다.

하지만 IDE에서 검색해서 리팩토링하기 어렵기 때문에 추천하지 않습니다.

1
2
3
4
T foo(T var1, T var2, T /*unused_parameter*/)
{
return var1 + var2;
}

 

std::ignore

std::ignore는 원래 std::tuple을 쓸 때 특정 변수를 무시하도록 만든 헬퍼 기능입니다.

근데 그냥 변수에도 쓸 수 있습니다.

그냥 변수에서 쓰라고 만든 기능은 아니지만, std::ignore라는 이름답게 개발자들이 읽을 때 ‘아 이 변수는 무시하라고 만든거구나’ 라고 이해할 수 있습니다.

이런 면에서는 void cast보다 훨씬 좋다고 생각합니다.

나중에 리팩토링할 때 std::ignore를 찾아서 고칠수도 있습니다.

1
2
3
4
5
T foo (T var1, T var2, T unused_parameter)
{
std::ignore = unused_parameter;
return var1 + var2;
}

 


추천하는 방법들

제일 좋은 방법들이라고 생각합니다.

 

C++17의 [[maybe_unused]] attribute

‘이 변수는 아마 안쓸수도?’

제일 좋은 방법입니다.

컴파일러도, 나도, 내 동료들도, 모두가 바로 이해하는 방법입니다.

단점이라면 C++17을 요구합니다. 현재 자율주행 쪽 C++ 프로그래밍 규제로 C++11에서 작업하는데, 이거 못써서 미칠 노릇입니다.

1
2
3
4
T foo (T var1, T var2, [[maybe_unused]] T unused_parameter)
{
return var1 + var2;
}

 

boost::ignore_unused()

Boost 프레임워크를 쓴다면 고려해볼만한 방법입니다.

C++11등에서 작업하는데 C++17/20의 기능을 불러오기 위해 Boost 프레임워크를 쓰는 경우가 있습니다.

boost::ignore_unused()는 boost의 내장함수이며, 좋은 함수 이름으로 unused parameter를 무시할 수 있게 해줍니다.

이거도 나중에 boost::ignore_unused로 찾아서 리팩토링 해주기 좋습니다.

1
2
3
4
5
6
7
#include "boost/core/ignore_unused.hpp"

T foo (T var1, T var2, T unused_parameter)
{
boost::ignore_unused(unused_paramter);
return var1 + var2;
}