I have some c++ code where I switch over values in an enum. I was trying to compile this with -Wall -Wextra -Werror. This is fine when using clang. GCC, however, complains that the default code path is not covered. A simplified version looks as follows:
enum class Types: int {
A,
B,
C
};
bool some_logic(Types val) {
switch (val) {
case Types::A:
return (false);
case Types::B:
return (true);
case Types::C:
return (false);
}
}
I can handle this by adding a default case, or another return statement at the end of the function. However, my question was why doesn't GCC detect that all of the cases of the enum are covered? Or phrased differently, is there a legitimate reason for GCC to complain here?
I've put a comparison of the compiler outputs here.
Because all the cases aren't covered.
It is possible to assign to val a value that is not named in the Types definition. Enums aren't constrained to just those values.
some_logic((Types)3); // whoops
If you're really, properly sure that some_logic will only be provided a value that you named in the Types definition, or wish to consider other cases to be "exceptional" precondition violations, that's fine but you'll still need to tell the computer that.
Opinion varies on the best approach, but in this case I'd leave out the default (so that you still get compiler warnings if you later add to Types and forget to update the switch) but plonk a simple throw afterwards:
bool some_logic(const Types val)
{
switch (val) {
case Types::A:
return (false);
case Types::B:
return (true);
case Types::C:
return (false);
}
throw std::runtime_error("Didn't expect that innit");
}
in the absence of any stronger requirements.
Related
I have an enum with one undefined and two user values:
class enum E
{
UNDEFINED,
VALUE1,
VALUE2
};
I want to add VALUE3 but I'm worried there's a lot of code like:
assert(val != E::UNDEFINED);
if(val == E::VALUE1)
{
}
else
{
// Without an assert this wrongly assumes E::VALUE2
}
and:
something = (val == E::VALUE1) ? a : b; // last part assumes E::VALUE2
I like that compilers warn against switch statements not handling all enumerations and wondered if there is anything similar to show all instances of the above?
I'm concerned I won't find and update all instances of the above.
Compiler is Clang
Enums are not restricted to the values you give names to. From cppreference (formatting is mine):
An enumeration is a distinct type whose value is restricted to a range of values (see below for details),
which may include several explicitly named constants ("enumerators"). The values of the constants are values of an integral type known as the underlying type of the enumeration.
The "below details" explain how the enums underlying type is determined. Out of this range we (usually) give names only to some values as in:
enum foo {A,B,C};
int main() {
foo x = static_cast<foo>(42);
}
This code is completely fine. x has an underlying value of 42. There is no name for the value but this doesn't really matter... unless you assume that it does.
That wrong assumption is made by this code:
assert(val != E::UNDEFINED);
if(val == E::VALUE1)
{
}
else
{
// Without an assert this wrongly assumes E::VALUE2
}
This code is what needs to be fixed (independent of whether you add a new named constant to the enum or not).
Now for a more serious trial to answer the question...
There is no way to get a warning when a chain of if-else does not cover all enum values. What you can do is to turn all if-else uses of the enum into errors. Consider what actually happens here:
if (x == E::VALUE1) do_something();
switch(x) {
case E::VALUE1 : return 1;
}
In the if statement we call operator==(foo,foo); its return value either is a bool or is implicitly converted to one. With the switch none of this is needed. We can make use of this to turn if-else usages of the enum into errors. Bear with me I will explain in two steps. First lets create a compiler error for if( x == E::VALUE1):
class helper {
operator bool(){ return false;}
};
helper operator==(E,E){
return {};
}
Now if (x == E::VALUE1) calls helper operator==(E,E), thats fine. Then the result is converted to bool, and that fails because the conversion is private. Using the enum in a switch is still ok and you can rely on compiler errors / warnings. The basic idea is just to have something that only fails to compile when called (in the wrong/right context). (Live Demo).
The drawback is that also all other used of operator== are broken. We can fix them by modifying the helper and the call sites:
#include <type_traits>
enum E {VALUE1};
struct helper {
bool value;
private:
operator bool(){ return false;}
};
helper operator==(E a,E b){
return {
static_cast<std::underlying_type_t<E>>(a) == static_cast<std::underlying_type_t<E>>(b)
};
}
int main() {
E x{VALUE1};
//if ( x== E::VALUE1); // ERROR
bool is_same = (x == E::VALUE1).value;
switch(x) {
case E::VALUE1 : return 1;
}
}
Yes it is a major inconvenience to have to write .value, but in this way you can turn all uses of the enum in ifs into errors while everything else will still compile. Also note that you have to make sure to cover all cases you want to catch (eg !=,<, etc).
I've read already a couple times (e.g. here Compiler: What if condition is always true / false) that any decent c++ compiler will opt-out something like
if(false)
{
...
}
But what if there is an intentional jump into this if(false) block. I'm having something like this in mind
#include <iostream>
void func(int part){
switch (part) {
case 0:{
if(false)
case 1:{std::cout << "hello" << std::endl;}
break;
}
default:
break;
}
}
int main()
{
func(0);
func(1);
return 0;
}
Is any decent c++ compiler going to respect the jump or will there eventually going to be some problems with opting-out?
The code doesn't appear to be Undefined Behavior. Therefore any optimizations are not allowed to produce any effects which would affect the behavior of the code.
Note: Related to this kind of code, one thing you are not allowed to do is "goto" over definitions of local variables. But this code doesn't do that, so no problem.
Another note: If you have this kind of code in a "real" (not toy, experiment, obfuscation exercise etc) program, you should really refactor it into something which doesn't elicit quite so many WTFs from anybody reading the code.
Found the following statement in Wiki:
C++11 introduced the concept of a constexpr-declared function; a
function which could be executed at compile time. Their return values
could be consumed by operations that require constant expressions,
such as an integer template argument. However, C++11 constexpr
functions could only contain a single expression that is returned (as
well as static_asserts and a small number of other declarations).
C++14 relaxes these restrictions. Constexpr-declared functions may now
contain the following: The conditional
...
branching statements if and switch
So, Is it actually possible to have a switch in a constexpr function in c++14/c++17? And, if possible, what syntax is for that?
For example, I'd like to have something like this:
enum class Terrain : std::uintmax_t {
ROAD,
SOIL,
GRASS,
MUD,
SNOW,
};
constexpr float
getStepPrice(Terrain const& terrain)
{
switch constexpr (terrain)
{
case Terrain::ROAD: return 1.0f;
...
}
}
Not exactly. In the case of if constexpr, you can rest assured that the resulting code has no branching. Moreover, discarded statements need not compile. Those are the guarantees I think you would expect from a true switch constexpr.
class Dog;
class Snake;
#define USE_IF
template<typename Pet>
constexpr void foo(Pet pet) {
#ifdef USE_IF
// This works
if constexpr(std::is_same_v<Pet, Dog>) pet.bark();
else pet.slither();
#else
// This doesn't
switch (std::is_same_v<Pet, Dog>) {
case true: pet.bark(); break; // <== Error if Snake
case false: pet.slither(); break; // <== Error if Dog
}
#else
}
BTW, I'm being a little nit-picky vis-a-vis Baum's accepted answer, which is fine, for practical puposes. I suspect you'd find good compilers will elide logically impossible bits from a switch-case statement with constexpr functions, as well as even non-constexpr inlined functions.
So, Is it actually possible to have a switch in a constexpr function in c++14/c++17?
Yes.
And, if possible, what syntax is for that?
There is absolutely nothing special about the syntax, it's just a normal switch. Like this:
constexpr int fun (int i) {
switch(i) {
case 0: return 7;
default: return 5;
}
}
int main () {
int arr[fun(3)];
}
Sun Studio 12.1 prints the warning
Warning: The last statement should return a value.
frequently for functions like that:
int f()
{
/* some code that may return */
// if we end up here, something is broken
throw std::runtime_error("Error ...");
}
It is perfectly clear that we do not need a return value at the end of the function. I hesitate to insert something like
// Silence a compiler warning
return 42;
at the end of such a function, since it is dead code anyway. For more complicated return types, it might actually be difficult to construct a 'sensible' bogus value.
What is the recommended way to silence such a warning?
Can you reorganize the code in the function in such a way (hopefully more logical as well) that the normal path happens at the end of the function so that a return can be used, and the exceptional path happens earlier, NOT as the last statement?
EDIT: If reorganizing the function really doesn't make sense, you can always just put a dummy return 0; with a comment. It's better to squelch the warning that way than more globally.
If you really want to quiet the warning permanently, you can use #pragma error_messages (off, wnoretvalue) but note that the warning really is useful most of the time so I absolutely don't suggest turning it off. You can use the on version of the pragma to re-enable the warning after the function, but the compiler will still emit the warning if your function is ever inlined. If you put the function in its own source file and use the pragma that should shush the warning relatively safely though, since it can't affect other translation units.
Another really wacky possibility is to switch to g++. Unless you're compiling for SPARC g++ may actually generate better code than Sun studio.
I find it a perfect spot for abort(). You should never end there, according to you, so something like:
UNREACHABLE("message")
which expands into:
#ifdef NDEBUG
#define UNREACHABLE(Message_) abort();
#else
#define UNREACHABLE(Message_) assert(0 && Message_);
#endif
Looks appropriate
Since you know the exception will be systematically called, why don't you simply return a 0?
Perhaps encapsulate the contents in a do { } while (false); construct:
int my_function()
{
int result = DEFAULT_VALUE;
do
{
result = /*...*/
// Whatever
if (error)
{
throw std::runtime_error("Error ...");
}
} while (false);
return result;
}
The idea is for normal operation to set the result value then let the execution flow to the end or use a break to jump to the return statement.
I don't know of a "recommended" way to deal with it, but to answer your question about coping with more complex types, what about:
ComplexType foo()
{
...
throw std::runtime( "Error..." );
return *(ComplexType*)(0);
}
This would then work with any return type. I realise it looks evil, but its there just to silence the warning. As you say, this code will never be executed, and it may even be optimised out.
I was trying to answer this question. As suggested by the accepted answer, the problem with that code is that not all control paths are returning a value. I tried this code on the VC9 compiler and it gave me a warning about the same. My question is why is just a warning and not an error? Also, in case the path which doesn't return a value gets executed, what will be returned by the function (It has to return something) ? Is it just whatever is there on top of the stack or is the dreaded undefined behavior again?
Failing to return a value from a function that has a non-void return type results in undefined behaviour, but is not a semantic error.
The reason for this, as far as I can determine, is largely historical.
C originally didn't have void and implicit int meant that most functions returned an int unless explicitly declared to return something else even if there was no intention to use the return value.
This means that a lot of functions returned an int but without explicitly setting a return value, but that was OK becase the callers would never use the return value for these functions.
Some functions did return a value, but used the implicit int because int was a suitable return type.
This means that pre-void code had lots of functions which nominally returned int but which could be declared to return void and lots of other functions that should return an int with no clear way to tell the difference. Enforcing return on all code paths of all non-void functions at any stage would break legacy code.
There is also the argument that some code paths in a function may be unreachable but this may not be easy to determine from a simple static analysis so why enforce an unnecessary return?
I would guess it is only a warning because the compiler cannot always be 100% sure it is possible to not hit a return.
i.e. if you had:
-= source1.c =-
int func()
{
if(doSomething())
{
return 0;
}
}
-= source2.c =-
int doSomething()
{
return 1;
}
The compiler in this case might not be able to know it will always hit the return, but you do. Of course this is terrible programming practice to rely on knowing how external code works.
As for what will actually be returned it depends on the platform. On x86 ABIs EAX is used for the return value (up to 32bits) so it will return what ever was placed in that register (which could be a return from something else, a temporary value or total garbage).
Technically it is not guaranteed to be an error if you call a function and that function always throws an exception. For example here is some pseudo code, and you know raiseError always throws.
MyClass func( params )
{
if( allIsValid() )
{
return myObject;
}
else
{
raiseError( errorInfo );
}
}
If the compiler cannot see the implementation of raiseError, it will not know that the function is going to throw. So really there is actually no undefined behaviour here. Of course it is good to silence the compiler here, which you can do with either writing a "dummy" return statement after raiseError, or a dummy "throw". I call them "dummy" because they will never be reached in reality. (You can also suppress the warning if you really insist). However there is no error or undefined behaviour.
here is another reason it isn't an error
the following will give you the same warning since the compiler expects you to return something from the catch block even though you're throwing there
int foo(){
try{
return bar(0);
} catch(std::exception& ex){
//do cleanup
throw ex;
}
}
int bar(unsigned int i){
if(i == 0){
throw std::string("Value must be greater than 0");
} else{
return 0;
}
}
Another example where it may be okay for some control paths to not return a value:
enum E : int {A, B};
int foo(E e) {
switch (e) {
case A: return 30;
case B: return 50;
}
}
It's possible that e won't be A or B, but the implication is that it always will be one of those values. If that's the case then the code is fine and there's no problem. Making this warning into a mandatory error would require unnecessary, 'unreachable' clutter.
If you want the warning to be an error anyway, you can configure your compiler to do that with a flag like /WX or -Werror. Though of course you should note that different compilers may make different determinations as to what's unreachable so you may be fixing different things for different compilers.
It is not an error because it may be the intended behaviour. For example, some encryption libraries use uninitialized local data as a step for seeding. As return values are kept in calling-convention and platform specific locations, this may help in some unusual (like the above) situations. In this case, the function returns whatever is left on the register used to return the return value.
Consider the following scenario:
UINT GenderID(GENDER gender)
{
switch(gender)
{
case MALE:
return MALE_ID;
case FEMALE:
return FEMALE_ID;
}
// default not required because [GENDER] in our 'Matrix' CAN be either M or F
}
a C++ complier should let you have your 'Matrix' your way; Thus its not an Error.