开发者

Compiler warning when switching on an enum

开发者 https://www.devze.com 2023-03-03 17:43 出处:网络
enum ENUM(Option1,Option2,Option3); string func(ENUM x) { switch(x) { case Option1: return \"Option1\"; case Option2: return \"Option2\";
enum ENUM(Option1,Option2,Option3);

string func(ENUM x)
{
 switch(x)
 {
  case Option1: return "Option1";
  case Option2: return "Option2";
  case Option3: return "Option3";
 }
}

This compiles and works but gives a compiler warning that not a开发者_StackOverflow中文版ll control paths return. However isn't the point that if you use enums properly, this isn't the case? If another ENUM val is added, I want compilation to fail but as long as all cases are covered I want it to compile warning-free.

Is this the compiler protecting against bad casted values, is it just part of C++ and needs to be lived with?


In C++, enums are not safe. You cannot expect an enum value to be one of the values defined in the enum declaration:

  • it could be uninitialized (thus garbage)
  • you could have an improper static_cast from a int

Therefore, the compiler cannot expect the switch to return, even if you cover all elements of your enum. However, it is truly an error condition, functionally speaking.

There are two ways to react:

  • add a default case to your enum
  • add a statement after the switch

In order to choose wisely, remember that the compiler may (if you ask it) trigger a warning whenever a switch does not cover all the cases of an enum, at the condition that there is no default statement. Smart compilers (ie Clang) allow to map warnings to errors individually, which greatly help catching those bugs.

Therefore, you have a decision to take:

  • if you want to be notified whenever you forget to change this method after updating the enum, then do not use default
  • if you want to be able to update the enum and ignore this switch, then use the default

Finally, you have to decide how to react, noting that using a runtime error is inconsistent with using a default statement (it's best to catch errors at compile-time whenever possible):

  • ignore the error and return some predefined value
  • throw an exception (with the enum value, please)
  • assert (and thus crash hard in debug, to get a memory dump, and do something else in release, like nothing, or throwing an exception)

My personal fav is a UNREACHABLE(Text_) macro, which provokes a memory dump in Debug (so that I get a full trace) and log an error and throw in Release (so that the server stops processing this request, but does not stop responding altogether).

This gives code like such:

char const* func(ENUM x)
{
 switch(x)
 {
  case Option1: return "Option1";
  case Option2: return "Option2";
  case Option3: return "Option3";
 }
 UNREACHABLE("func(ENUM)")
}


From the compilers point of view, the type of the enum is an integer, so it's still possible that the value of x is one of the other cases.

Normally, I would add a default: label that triggers an internal error.

Hint: If you wrap the call the the intern error in an infinite loop, you don't have to invent a bogus return value. For example:

#define IntErr(x) for(;;) { InternalError(x); }
string func(ENUM x)
{
  switch(x)
  {
  case Option1: return "Option1";
  case Option2: return "Option2";
  case Option3: return "Option3";
  default: IntErr("Unexpected ENUM value");
 }
}


What happens if for some reason x is neither Option1, nor Option2, nor Option3?

Sure, you could argue that will never happen, but since the method has to return something, you have two options:

  • add a return string(""); at the end.

  • add a default to the switch that returns string("").

As CodeGray points out, the second option is arguably better style. You could also return something other than an empty string.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号