I've got a problem where I want to do one of three things...
if the value of x is 1-5 (inclusive) do A, if x is between 6-13 (inclusive) do B, and if x is between 14-16 do C.
I figured the switch case would be ok, although I guess I could use a plain IF / ELSE IF, however, as I coded it, I can't help but think there is a more elegant way of stating this USING the switch/case (just in case I encounter a similar need that has more then three options).
here is what I have:
switch ( x ) {
case 1:case 2:case 3:case 4:case 5:
// DO A
break;
case 6:case 7:case 8:case 9:case 10:case 11:case 12:case 13:
// DO B
break;
case 14:case 15:case 16:
// DO C
break;
}
is there a way in the case to specify "between" (inclusive or exclusive)?
thanks
Nope. Switch statement are designed to work with single, constant values. Unless the comparison is such that the value can be modified to conform to that rule, the only options are what you have written already OR using if/else if/else, AFAIK. In most cases, the latter is cleaner than a bunch of hard coded case statements IMO.
Related
As in title. There are a lot similar questions but I will give different example: I have 2 enums
enum A
{
A_ONE,
A_TWO
};
enum B
{
B_ONE,
B_TWO
};
What is more clear switch by enum A and then in all cases switch by enum B?
A type1;
B type2;
switch(type1)
{
case A_ONE:
switch(type2)
{
case B_ONE:
//statement1
break;
case B_TWO:
//statement2
break;
}
break;
case A_TWO:
switch(type2)
{
case B_ONE:
//statement3
break;
case B_TWO:
//statement4
break;
}
break;
}
or using else if
if(type1 == A_ONE && type2 == B_ONE)
//statement1
else if(type1 == A_ONE && type2 == B_TWO)
//statement2
else if(type1 == A_TWO && type2 == B_ONE)
//statement3
else if(type1 == A_TWO && type2 == B_TWO)
//statement4
Which is better practice? What do you preffer
It's more of a style issue than anything else. If you are only checking for the presence of two conditions over limited sets of data, the switch() approach is easier to follow, and less prone to issues (forgetting the final else to go with if, else if; using the assignment operator = instead of the equivalence operator ==; accidentally using the binary bitwise AND & operator instead of the binary logical AND && operator, etc).
The only potential downside of the switch() approach is to forget to put a break statement under each case, but you can use CppCheck or enable -Wswitch-fallthrough to cause compiler warnings or failures in such an event.
Edit
Forgot to mention to always have a default case in switches. I always assume it's a given.
So, use:
-Wswitch-default: force default cases in switch statements.
I stand corrected, -Wswitch-fallthrough is not yet implemented. Too bad, since clang has had it for a while. Use CppCheck as part of your build/QA process to avoid getting bit by this oversight.
I suggest you this:
Use 'else-if' statements if the amount of data you´re evaluating is low.
Use 'switch' when there is a considerable amount of data to test.
In your example there are only 4 posibble combinations, so it´s ok to do it that way, but think for example: what if your enum A would have 10 elements and enum B would have 6, yo would need 60 'else-if' statements to cover all posibilities, which later when you try to read it may become difficult, instead if you would use 'switch' you also need 60 declarations, but later you will find it more easy for read or modification
It depends.
I think it is a matter of taste, personal preferences or company standards.
In the case of two enumeration literals if-else statement may be equivalent to the switch statement. But as the number of literals grows the if-else statements may become unmaintainable or difficult to read, while the switch statement may help to systematically cover all cases.
Additionally I would suggest to consider putting default case to cover abnormal circumstances.
I wanted to use macro functions in switch statements before I realized the statements need to be constant. Example (does not compile):
#define BAND_FIELD1(B) (10 * B + 1)
...
#define BAND_FIELD7(B) (10 * B + 7)
int B = myField % 10;
switch (myField) {
case BAND_FIELD1(B):
variable1[B] = 123;
break;
case BAND_FIELD7(B):
variable7[B] = 321;
break;
...
}
I rather had to use if .. else:
if (myField == BAND_FIELD1(B)
variable1[B] = 123;
else if (myField == BAND_FIELD7(B)
variable7[B] = 321;
Why are the C++ switch statements limited to constant expressions?
One of the strengths of C++ is its static checking. The switch statement is a static control flow construct, whose power lies in the ability to check (statically) whether all cases have been considered, and in being able to group cases sensibly (e.g. fall through common parts).
If you want to check conditions dynamically, you can already do so with a variety of techniques (if statements, conditional operators, associative arrays, virtual functions, to name a few).
The compiler can generate the fastest possible code for a switch when presented with constants--e.g. jump tables or binary search trees.
When given non-constant values, it can't generate code that's any faster than chained if-else statements. Which you already have at your disposal anyway!
Why are the c++ switch statements limited to constant expressions?
Because the check performed by the switch statements are static. This means that the expressions need to be known at compile time.
In C++11 you can use constexpr (if the expressions are derivated by other constant expressions) in your favor. For example consider this function (that replaces your #define):
inline constexpr int BAND_FIELD1(int B) {
return 10 * B + 1;
}
used in the following simplified version of your code:
constexpr int myField = 0;
constexpr int B = myField % 10;
int variable1 = 0;
switch (myField) {
case BAND_FIELD1(B):
variable1 = 123;
break;
// ...
default: break;
}
As you can see, the above code will easily compile.
My answer would be that the C++ switch is a leftover from the C switch, which is a leftover from antique languages like PL/M.
This case uniqueness is just a chance byproduct of a construct that dates back from the 70's, in my opinion.
It does not guarantee in any way that all cases have been covered, especially given the weak typing of C++ enums.
Considering the piles of assembly code C++ routinely generates behind the scene, arguing that C++ switch is limited to constants for performance reasons seems a bit rich to me.
Many other languages support variables and/or non-numeric expressions in switch statements, and I haven't seen many programmers complain about the possible duplicate case values.
To help answer the question, consider the following (IF it was legal):
int a=15;
int b=16;
int c=15;
int value = a;
switch (value)
{
case a:
// Value is A
break;
case b:
// value is B
break;
case c:
// value is C
break;
default:
// value is unknown
}
We would never execute the code for C, because A would be executed instead. The purpose of switch is that check a value which has unique, testable values. Its a clean if..else if..else..if..else.
Compilers analyze the constant values at compile time to generate optimized lookup tables and decision trees to select a case quickly.
For example, if a table can’t be used, the compiler may generate efficient trinary decision trees using sets of 3 cpu instructions: compare to a value to set cpu flags, branch lower to other cases, branch higher to other cases, or fall through to the equal case.
Remember that C/C++ is concerned with performance.
Maybe a new syntax will be added or compilers will be expanded in the future, but standards advancements try to be incremental and not break code that was already written, or invalidate existing compilers.
This question already has answers here:
How do I select a range of values in a switch statement?
(18 answers)
Closed 9 years ago.
Am I able to add sets instead of numbers to "switch" statement? Like:
switch(number)
{
case 1<50: //if number is between 1 and 50
{
blah;
break;
}
case 50<100: //if number is between 50 and 100
{
blah;
break;
}
and so on.
(Edit following comments below, acknowledgement given to user syam)
No. In C++ you can only switch on things that reduce to an integer. You'll need to build a function that computes integral results for 1 < 50 and 50 < 100 etc. and use switch(thatFunction(...)).
Or, if you don't need the follow-though idiom that a switch gives you (by the looks of your example, you don't), just use if, else if, else.
Extract from below comments:
6.4.2-2 [stmt.switch] The condition shall be of integral type, enumeration type, or of a class type for which a single non-explicit conversion function to integral or enumeration type exists [...] the constant-expression shall be a converted constant expression (5.19) of the promoted type of the switch condition
1 < 50 is a boolean expression that gets a compile-time value of true, and thus becomes 1 in an integer context. So you end up with two identical cases in your switch. Compile with high warning level - your compiler will surely complain.
Sadly not. You can use fall-through for a small set of discrete values:
case 1:
case 2:
blah;
break;
but for large ranges the only sensible option is if...else.
For me, this works on my g++ (GCC) 4.7.2 20121109
#include <iostream>
using namespace std;
int main() {
switch(6) {
case 1 ... 5:
cout << "Between 1 and 5" << endl;
break;
case 6 ... 10:
cout << "Between 6 and 10" << endl;
break;
}
}
Thanks for stating in the comments that it's GCC's extenstion
Conditionals are not permitted as case statements, so your option is to use if / else statements.
You can't do that in C++. case switches must be a constant that can be converted to an int.
This is nicely discussed here.
A switch statement is useful when you have to use a single variable across many possible conditions. A switch statement works faster than an if-else block by using a jump table. It requires constant integers when it makes the table. The reason expressions that dont evaluate to a fixed value are not allowed is to save from ambiguity.
case 1+a;
break;
case 5;
break;
Now what if a is 4. That will cause issues. If it doesnt use the jump table it wont be much useful than an if-else block
The below is the code which I need to optimize and planned that it would be good to move to the switch construct. But I can compare in case. So I planned to make the comparison (len > 3) as the default case.
If I make the comparison part (len > 3) as the default case and add the default as the first in the switch, will it be faster?
Or how can I make the below code as a switch statement?
if ( len > 3 ) {
// Which will happen more often;
}
else if ( len == 3 ) {
// Next case which may occur often;
} else if ( len == 2 ) {
// The next priority case;
} else {
// And this case occurs rarely;
}
Probably not. Both if...else and switch...case are high-level constructs. What slows you down is the branch prediction. The better your prediction is the faster your code will run. You should put your most occurring case in first if, second in the else if and so on, just like you wrote. For switch the result depends on the internal compiler implementation which can reorder the cases despite your own order. The default should be actually reserved for the less occurring situations because rest of the conditions must be checked before falling back to default.
To conclude all this, performance-wise usage of if...else is optimal as long as you set your conditions in the correct order. Regarding switch...case it's compiler specific and depends on the applied optimizations.
Also note that switch...case is more limited than if...else since it supports only simple comparison of values.
Although you've accepted what is probably the best answer, I wanted to provide an alternative.
Note that the standard caveat applies - optimisation isn't optimisation unless you've profiled your code.
However if you are encountering poor performance relating to branches, you can reduce or eliminate them. That your code has one or more inequality comparisons is not an obstacle - you can reduce your cases down to a set of direct equalities, and if necessary use that to index a table, rather than branch at all.
void doSomething(int len)
{
static const char* str[] =
{ "%2d > 3\n",
"%2d < 2\n",
"%2d = 2\n",
"%2d = 3\n"
};
int m1 = (len-2)>>31;
int m2 = (len-4)>>31;
int r = (len & m2 & ~m1) + !!m1;
printf(str[r],len);
}
Note that this codes makes several assumptions which may not hold in practice, but as we're making the wild assumption that this even needs optimising in the first place...
Also, note that better optimisations may be possible with more knowledge about the actual range and type of the input parameter, and indeed what the actual actions taken need to be.
You can't move comparisons into a switch statement... it uses single checks for its selections.. i.e.,
switch (len) {
case 1:
// Do case 1 stuff here
break;
case 2:
// Do case 2 stuff here
break;
case 3:
// Do case 3 stuff here
break;
}
Use breaks to prevent the case statements from running into each other. Read more here.
Your code is as 'optimized' as it will get in its current state...
The only way you're going to know is to benchmark it with your
compiler. If performance is an issue, you should use the option
to provide the compiler with profiler output, and let it decide;
it will generally find the best solution. (Note that even on
a specific architecture, like Intel, the best solution in terms
of machine instructions may vary from one processor to the
next.)
In your case, the switch would probably look like:
switch ( len ) {
case 2:
// ...
break;
case 3:
// ...
break;
default:
if ( len > 3 ) {
// ...
} else {
// ...
}
}
With only two effective cases, the compiler doesn't have much to
work with. A typical implementation (without extreme
optimization) would do bounds checking, then a table lookup for
the two explicit cases. Any decent compiler will then pick up
that the comparison in your default case corresponds to one of
the bounds checks it has already done, and not duplicate it.
But with only two cases, the jump table will probably not make
a significant difference compared to the two comparisons,
especially as you'll be out of bounds in the most frequent case.
Until you have actual profiler information that this is
a bottleneck in your code, I wouldn't worry about it. Once you
have that information, you can profile different variants to see
which is faster, but I suspect that if you use maximum
optimization and feed profiling information back into the
compiler, there will be no difference.
If you are worried about speed, the truth is that your if...else or switch...case statements won't have a real impact of your application speed unless you have hundreds of them. The places where you lose speed is in your iterations or loops. To answer your question specifically, you cannot convert your if...else statement to a switch...case statement with the default appearing first; but with that said, if you did convert to a switch...case then you will that they run at the same speed (difference is too minute to be picked up by conventional benchmarking tools).
You can use a range in a case:
switch (len) {
case 3 ... INT_MAX:
// ...
break;
case 2:
// ...
break;
default:
// ...
break;
}
But that is an extension provided by GCC...
I wrote the following code:
int i = 0;
switch(i++)
{
case 0:
cout << 0;
case 1:
cout << 1;
}
cout << "\n" << i;
The output of the code was like this:
01
1
Can anyone please explain the first line of output? Why are 0 and 1 both being printed?
First, the expression i++ (post-increment operator) evaluates to 0 (even though it sets the value of i to 1). So inside the switch, the case 0: branch is selected.
Then, because there is no break after your case 0:, the program continues with executing the code in the case 1: label.
Summing it up, you have: 0 from the first switch branch, 1 from the second branch, and another 1 because that's the final value of i.
Because you need to add a break after each case, which prevents execution of the following statements. E.g.
switch(i++)
{
case 0:
cout<<0;
break;
case 1:
cout<<1;
break;
}
Admittedly, the second break is superfluous but I put it there just for the sake of consistency
you need to put "break;" at the end of each case.
switch is a strange construct. It comes from C, and Java and C# adopted it too, so it is not considered totally "non-OO".
switch on state which changes is a valid OO concept, but often is used for switching based on type.
In particular the compiler usually creates a "jump" table which means it is O(1) what block of code gets called, unlike a nested "if" statement. You may have multiple values (not including default) jump to the same point, thus code blocks "run into" each other unless you explicitly insert a "break" statement.
This is how it was done in C and has been retained for C++.
With regards to the value in the switch, it must be a numeric value but does not have to be a constant. In your case i++ evaluates to 0 but increments i to 1. This is well-defined behaviour and there is no issues with sequence points here.