GCC switch on enum, retain missing warning but use default
Using GCC, if you switch
on an enum
value and one of the enums is missing a case
statement a warning will be emitted. When you add a default
item the warning will no longer be em开发者_StackOverflowitted, which makes sense in the general case.
Is there a way to use a default
statement and still have a warning if not all the enum
values are covered? Since my function may deal with impure input I'd like to cover the generic case but still get compiler warnings about missing an enum case.
Currently I end up assigning a default after the switch statement.
-Wswitch-enum, but unluckily only the most recent version supports this.
(You could of course simulate the behaviour that you want by using a goto outside the switch and omitting the default, but I would strongly advise against that, it's ugly and someone else reading your code would have a WTF experience.)
After reading the link from "David Rodríguez - dribeas", I thought it would be helpful to summarise the options listed there.
There are two ways to do this - either turn the messages about missing enum cases on for all switch
statements, then disable it for the ones you don't care about, or leave it at the default and force the errors on for those switch
statements you really care about.
Option 1: Warnings for all, mark some as silent
First, add -Wswitch-enum
to your compiler flags, so that all switch
statements, even ones with a default
clause, will have warnings generated if an enum is not handled.
Then, for those switch
statements where you want the default
case to take care of things and don't want to see warnings, wrap the switch
statement like this:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"
switch () {
...
}
#pragma GCC diagnostic pop
This will temporarily disable the -Wswitch-enum
flag (hiding the warnings about missing enum cases) just for that case statement.
Option 2: Only warn when flagged to do so
Since the default GCC behaviour is to hide warnings when a default
clause is present, the compiler flags don't need to be changed for this option.
Instead, for those switch
statements that include a default
clause, but you still want to see warnings about missing enum cases, wrap the switch
like this:
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wswitch-enum"
switch () {
...
}
#pragma GCC diagnostic pop
This temporarily enables the -Wswitch-enum
flag between the push
and pop
lines, causing messages about missing enum cases to be displayed even when a default
clause is present. You can change the word warning
to error
if you want compilation to fail on a missing case.
I would say that the issue is more at the enum
level.
What I mean is that you should first validate your input (ie make sure that it does indeed map into a real enum) and only once validated, should you use the enum, in which case the default becomes redundant.
In order to validate the input, a simple solution I use is to create the enum through a macro which will also automatically generate converting functions: from/to string
, from int
(or whatever).
For example:
DEFINE_ENUM_DETAILED(SomeEnum, int, (Foo, 0, "Foo")(Bar, 1, "Bar"));
Could generate:
struct SomeEnum {
enum { Foo = 0, Bar = 1 } type;
type FromString(std::string const& s) {
if (s == "Foo") { return Foo; }
if (s == "Bar") { return Bar; }
assert(0 && "SomeEnum::FromString - unknown value");
}
std::string ToString(type e) {
switch(e) {
case Foo: return "Foo";
case Bar: return "Bar";
}
}
type FromIntegral(int i) {
switch(i) {
case Foo: return Foo;
case Bar: return Bar;
}
assert(0 && "SomeEnum::FromIntegral- unknown value");
}
};
It's the only way I have found to generate this easily (though the string conversion is here a bit simplified here).
Another solution would be to use a script to generate the source code from an alternative file.
EDIT: Having the switch operating and validating
The simple answer is (like I did above) to let the program flow out of the switch instead of using a default clause. This is possible if normal flow (when falling into a case
) does not end up falling out of the switch.
switch(event) {
case Foo: {
// bla
return 0;
}
case Bar: {
return 0;
}
}
unreachable("should never have got there");
Unfortunately, as of today, neither gcc nor llvm can detect you are not comparing all the values of an enum
in a switch
if you include a default
switch.
One reason would be that you must be able to write warning-free code without breaking your back. If, for example, you have an enum with 100 constants, then you would have to list each and every one of them in every switch statement, even if you only needed to inspect a handful of them, if the warning should work as you suggested.
精彩评论