How to Disable a Warning in C++
As explained in item 53 of Effective C++, you should “Pay attention to compiler warnings”. In the vast majority of cases, the compiler has a good reason to emit them, and in the vast majority of cases, they point out to an oversight in your code.
But in a minority of cases, you may want to deliberately write code that triggers a warning.
In such occasions, letting the warning in the compiler’s output has several drawbacks. First, you will no longer have a clean build with no errors and no warnings. This warning will always remain here, and you’ll have to check that it’s the one you decided to leave in every time you compile the code.
This doesn’t scale if there are several warnings you decide to leave, because at each build you’ll have to check them all to see if a new warning hasn’t popped up and needs to be checked.
Second, if you’re following the best practice of transforming warnings into errors, by activating the -Werror
flag in gcc and clang for example, leaving a warning in is simply not an option.
Fortunately, C++ lets you block the emission of a specific warning for a portion of code. Let’s see how to do that and keep code expressive.
Different code for different compilers
Let’s take the example of the warning that warns you that you didn’t use one of the parameters of a function:
void f(int a, int b) { std::cout << a << '\n'; // we are not using b! }
The compiler is able to emit a warning for this. But all compilers don’t emit the same warning.
Here is gcc’s warning, which is the same as clang’s:
warning: unused parameter 'b' [-Wunused-parameter]
And here is Visual Studio’s warning:
warning C4100: 'b': unreferenced formal parameter
You can observe that they don’t have the same text and–more importantly for our purpose–the warning is not identified the same way.
Visual Studio identifies warnings with a number (here, 4100), whereas gcc and clang use a string (here, -Wunused-parameter
).
As you can imagine, that will lead to different code to disable the same warning between the compilers.
We’re going to see how to disable a warning on gcc, clang, and on Visual Studio, and in case your application has to compile on all three, how to write code that disable a warning on all compilers.
The disabling sequence
Before we get into the code for each compiler, there is something in common in the sequence of disabling a warning between all three compilers.
To disable a set of warnings for a given piece of code, you have to start with a “push” pre-processor instruction, then with a disabling instruction for each of the warning you want to suppress, and finish with a “pop” pre-processor instruction.
For example, in our case, the sequence would look like that:
// up until this line, the warning is active // PUSH disable warning (instruction specific to the compiler, see below) // DISABLE the warning that a parameter is not used void f(int a, int b) { std::cout << a << '\n'; // we are not using b, but the compiler won't emit a warning } // POP disable warning, the warning is now active again
Now let’s dive into the code for each compiler.
Disabling a warning on gcc and clang
A good thing is that gcc and clang require the exact same code for disabling a warning, as far as I’m aware.
The push instruction is this:
#pragma GCC diagnostic push
Note that even though it says “GCC”, it also works for clang.
The pop instruction is this:
#pragma GCC diagnostic pop
And to disable a warning, you indicate it this way:
#pragma GCC diagnostic ignored "-Wunused-parameter"
Putting this together, to suppress the warning in our example code we write:
#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" void f(int a, int b) { std::cout << a << '\n'; // we are not using b! } #pragma GCC diagnostic pop
That’s is for gcc and clang.
Disabling a warning on Visual Studio
With Visual Studio, the push instruction is this:
#pragma warning( push )
The pop instruction is this:
#pragma warning( pop )
And to disable a specific warning, we need to write code like this:
#pragma warning( disable : 4100 )
Remember, in Visual Studio warnings are identified with numbers, not names.
If we have to suppress the warning in our example code on Visual Studio, we would write this:
#pragma warning( push ) #pragma warning( disable : 4100 ) void f(int a, int b) { std::cout << a << '\n'; // we are not using b! } #pragma warning( pop )
All in all, this is not so complicated.
But what if you write code that needs to compile on gcc, clang AND Visual Studio?
That can happen if your application is deployed on multiple OSes, or if you write a library for the general population of C++ programmers.
This is where the fun begins.
Disabling a warning on gcc, clang and Visual Studio at the same time
Since disabling warnings is done at the level of the pre-processor, we’re going to need a macro. We need to write a macro that resolves to either one of the above pieces of code, depending on the compiler used.
The disabling sequence is similar between all three compilers, so we’ll write a macro for each of the three steps: push, disable and pop:
DISABLE_WARNING_PUSH DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER void f(int a, int b) { std::cout << a << '\n'; // we are not using b! } DISABLE_WARNING_POP
Let’s see how to write each macro on the various compilers, and then how to write code to combine all this into a cross-compiler code. We’re going to have some macro fun.
Implementing the macros on gcc and clang
As we saw above, the push in gcc and clang is this:
#pragma GCC diagnostic push
Our first instinct could be to define the DISABLE_WARNING_PUSH
like this:
#define PIPES_DISABLE_WARNING_PUSH #pragma(GCC diagnostic push)
But using DISABLE_WARNING_PUSH
then fails to compile:
error: expected unqualified-id DISABLE_WARNING_PUSH ^ note: expanded from macro 'DISABLE_WARNING_PUSH' #define DISABLE_WARNING_PUSH #pragma(GCC diagnostic push)
It’s because we’re not allowed to use #pragma
in a #define
instruction.
To circumvent this problem, compilers commonly offers a “pragma operator”, that is not standard and differs across compilers.
In gcc and clang, it is called _Pragma
, and can be used this way:
#define DISABLE_WARNING_PUSH _Pragma("GCC diagnostic push")
Note that _Pragma
expects a string with quotes, hence the "GCC diagnostic push"
.
Similarly, the pop instruction is this:
#define DISABLE_WARNING_POP _Pragma("GCC diagnostic pop")
Now to disable the warning, we have to write this:
#define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER _Pragma("GCC diagnostic ignored \"-Wunused-parameter\"")
Note the \"
around the name of the warning. Remember that gcc and clang identify warning with strings, and that _Pragma
expects a string. This results in a string within a string, so quotes inside of quotes, which then need to be escaped.
This is not pretty. To mitigate this, we could use C++11’s raw strings literals:
#define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER _Pragma(R"(GCC diagnostic ignored "-Wunused-parameter")")
But this is still far from ideal. Especially if we want to disable several types of warnings, because we’d need to repeat this code over and over.
What would be nice would be to write this:
#define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER DISABLE_WARNING(-Wunused-parameter)
With no quotes, just the name of the warning.
Let’s see how to do that. This where the macro fun begins.
Generic macro code
To get rid of all the issues with quotes, we’re going to use the “Stringizing opeator”, which is #
. As Microsoft Docs puts it, “If [the stringizing opeator] precedes a formal parameter in the macro definition, the actual argument passed by the macro invocation is enclosed in quotation marks and treated as a string literal.”
Put another way, the #
operator puts quotes around a macro parameter.
The stringizing operator helps support the DO_PRAGMA trick, that consists in defining the following macro:
#define DO_PRAGMA(X) _Pragma(#X)
In short, DO_PRAGMA
puts quotes around a string and passes it to the _Pragma
operator.
We’re going to use it this way (we’ll see how that works step by step afterwards):
#define DISABLE_WARNING(warningName) \ DO_PRAGMA(GCC diagnostic ignored #warningName)
DISABLE_WARNING
is a macro function that takes a parameter, which we can invoke like this:
DISABLE_WARNING(-Wunused-parameter)
In this case, warningName
is -Wunused-parameter
. So #warningName
, with the stringizing operator, is "-Wunused-parameter"
.
Thus,
GCC diagnostic ignored #warningName
is equivalent to
GCC diagnostic ignored "-Wunused-parameter"
Finally, DO_PRAGMA(GCC diagnostic ignored #warningName)
puts quotes around all that and sends it to _Pragma
. Which leads to the desired result.
As a result, this macro function allows to disable several warnings with expressive code:
#define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER DISABLE_WARNING(-Wunused-parameter) #define DISABLE_WARNING_UNREFERENCED_FUNCTION DISABLE_WARNING(-Wunused-function) // and so on
Implementing the macro in Visual Studio
If you came out of the preceding section in one piece, the rest should glide.
Visual Studio follows the same principles as gcc and clang: you can’t put a #pragma
inside of a #define
directive, but there is a pragma operator to help us achieve this. But contrary to gcc, it is not called _Pragma
but __pragma
, with two underscores.
What’s easier in Visual Studio than in gcc and clang is that the warnings are not identified by strings but by numbers (e.g. 4100), and the __pragma
operator doesn’t expect strings in quotes.
So here is how to write DISABLE_WARNING
for Visual Studio:
#define DISABLE_WARNING(warningNumber) __pragma(warning( disable : warningNumber ))
The push and the pop are also straightforward:
#define DISABLE_WARNING_PUSH __pragma(warning( push )) #define DISABLE_WARNING_POP __pragma(warning( pop ))
Putting it all together
Now that we know how to disable a warning for gcc, clang and Visual Studio, let’s put this altogether in the same code, so that your application or library can run on all three compilers with the same code.
Essentially, the code is going to follow this structure:
if Visual Studio code for Visual Studio else if gcc or clang code for gcc and clang else macros that are defined but don't do anything
To identity the compiler, we can rely on the specific macro that each of them defines:
_MSC_VER
for Visual Studio (which incidentally also gives the version of the compiler, but we won’t use this information),__GNUC__
for gcc,__clang__
for clang.
You’ll note that they use the naming convention that C++ programmers are not allowed to use: two consecutive underscores, and a name starting with an underscore followed by a capital letter. The very reason why we can’t use them is because they are reserved to the compiler. Like here.
Note the else
part in the above code. I think it is necessary to define the same macros as in the if
and else if
branches. Even if you don’t use another compiler than Visual Studio, gcc or clang today, it would be a shame to halt the compilation on another compiler just because you didn’t define the macros for it.
Or perhaps you don’t want your code to run on a compiler you don’t officially support. In any case, if this is what you want then a better option is to write somewhere else some specific macro-code to prevent the code from compile on non-supported compilers.
In summary, here is all the code put together:
#if defined(_MSC_VER) #define DISABLE_WARNING_PUSH __pragma(warning( push )) #define DISABLE_WARNING_POP __pragma(warning( pop )) #define DISABLE_WARNING(warningNumber) __pragma(warning( disable : warningNumber )) #define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER DISABLE_WARNING(4100) #define DISABLE_WARNING_UNREFERENCED_FUNCTION DISABLE_WARNING(4505) // other warnings you want to deactivate... #elif defined(__GNUC__) || defined(__clang__) #define DO_PRAGMA(X) _Pragma(#X) #define DISABLE_WARNING_PUSH DO_PRAGMA(GCC diagnostic push) #define DISABLE_WARNING_POP DO_PRAGMA(GCC diagnostic pop) #define DISABLE_WARNING(warningName) DO_PRAGMA(GCC diagnostic ignored #warningName) #define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER DISABLE_WARNING(-Wunused-parameter) #define DISABLE_WARNING_UNREFERENCED_FUNCTION DISABLE_WARNING(-Wunused-function) // other warnings you want to deactivate... #else #define DISABLE_WARNING_PUSH #define DISABLE_WARNING_POP #define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER #define DISABLE_WARNING_UNREFERENCED_FUNCTION // other warnings you want to deactivate... #endif
You can then use the macros this way:
DISABLE_WARNING_PUSH DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER DISABLE_WARNING_UNREFERENCED_FUNCTION /* code where you want to disable the warnings */ DISABLE_WARNING_POP
A great responsibility
This leads to code that is both concise and portable across compilers. Indeed, if you need to support a new compiler, you can just add a new branch to the #if defined
statement.
But before you get into all this, heed the advice of Effective C++ and “Pay attention to compiler warnings.” Only once you did that, and if you know what you’re doing, use the above code to silence a warning in a portion of your code.
You will also like
- What Books to Read to Get Better In C++
- How to split a string in C++
- Better Macros, Better Flags
- 3 Types of Macros That Improve C++ Code
- TODO_BEFORE(): A Cleaner Codebase for 2019
Share this post!