Compiler problem: switch statement

I believe the arduino SWITCH statement has some compile time problems.

If I try to declare a boolean variable in a CASE statement as illustrated in "case 2: in the snippet below, the compiler throws an error.

I add this to the other CASE statement problem I flagged earlier: ie. the compiler does not throw an error if you misspell "default" as "defalut.")

    switch (var) {
    case 1:
      //do something when var equals 1
      break;
    case 2:
      boolean X;
      //do something when var equals 2
      break;
    default:
      // if nothing else matches, do the default
      // default is optional
    break;
  }

This appears to be a problem with the compiler, NOT my code, and it seems to happen only with nested SWITCH statements.

Not a complaint -- just trying to flag an issue for others that may experience the same problem.

If I try to declare a boolean variable in a CASE statement as illustrated in "case 2: in the snippet below, the compiler throws an error.

Put the code block for the case in { and } to limit the scope of the variable being declared and you won't have the problem

(deleted)

Hi Curt,

This caught me out a while ago. It will compile but not work as expected. Took me ages to find out why.

If you declare a variable within a switch statement the compiler does not know what its scope is, which resutls in things not working as you'd expect.

The solution is to either declare the variable outside the switch or to define the scope within the case using {} as appropriate.

a bool is not a valid expression to use

Can you provide a reference for that ?

(deleted)

spycatcher2k:
From the link above.

I can't find any mention of bool on that page

Thanks for the replies. Moving all my variable declarations outside the scope of all of my nested switch blocks does indeed solve the problem.

The errors the compiler throws otherwise are ... interesting :slight_smile:

I've not had a chance to explore this issue (or the "defalut" issue) with the Gnu C compiler, but do note that the problem isn't discussed in the Gnu C reference manual either.

Putting all the code associated with a case in braces {} makes sense, and I'd suggest changing the section in the arduino reference manual's switch statement definition to include them. Two more keystrokes is a small price to pay...

spycatcher2k:
From the link above.

Why are you giving reference to C? Arduino code complies with C++.
In C, there are no dedicated(primitive) boolean/bool type. Its type is user defined type. And often they are defined as a char.

@CurtCarpenter: What error is the compiler giving you? Miss spelling "defalut:" does not raise an error before it is interpreted as a control label (for goto) instead of a case label.

It is staggeringly unlikely that the compiler has outright bugs in parsing the language. What is far more likeley is that someone doesn't understand the language as well as they think.

C++ supports the goto keyword and labels. deflat is a valid label.

if(foo) goto deflat;
switch(x) {
  case 1: do_something();
  deflat: do_something_else();
}

Ugly, but legit. Google "duff's device".

Yes, "ugly but legal" is pretty much why C and its descendants are my least favorite languages. But I enjoy the learning experience, and have used worse.

This is a well-known "lesser-known" limitation :slight_smile: of the C/C++ languages, related to the fact that the case labels are ... just labels. You can find a lot of discussion doing a web search for "c++ local variables in case", but I'm not sure any of them are very good at explaining why it's a problem for uninitialized variables.

I've really appreciated the explanations here and understand now what's happening. The computational linguist in me wants to scream "bad design!" anyway though :slight_smile: I imagine the issue is an echo of a compiler trade-off made in the early days, when every byte counted so much and a lot of decisions were made to enable the whole compiler/linker to fit into 64K bytes or less.

spycatcher2k:
a bool is not a valid expression to use.

But it does work and compiles without error or warning.

// test of bool for case statement

bool decide;
byte count;
unsigned long preset;

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  preset = millis();
}

void loop() {
  if (millis() - preset > 1500) {
    count++;
    preset = millis();
    Serial.println(count);
  }
  
  decide = (count & 0x1);

  switch (decide) {
    case 1:
      digitalWrite(LED_BUILTIN, HIGH);
      break;
    case 0:
      digitalWrite(LED_BUILTIN, LOW);
      break;
  }
}

I'm guessing the bool is promoted to int?

PaulMurrayCbr:
C++ supports the goto keyword and labels. deflat is a valid label.

You can jump into the middle of a switch statement from outside of it???

Yikes!

I used a single GOTO in my dissertation when I realized I was maintaining two identical blocks of code and the flow wouldn’t let me use more “desirable” structures (I already had two entry points, iirc).

I used another on an Arduino last year to break out of an inner loop (instead of testing in the outer loop). [I do find it odd that C lacks some kind of exit command for this . . .]

So about every 20 years, I’ve found GOTO not just useful but cleaner than the alternative . . .

dochawk:
You can jump into the middle of a switch statement from outside of it???

Yikes!

As I mentioned - check out duff's device. Not exactly the same thing, but similarly horrible. A switch statement, utlimately, compiles down to a jump table. There's nothing at all stopping the compiler from just allowing you to jump all over the place. Slightly more modern languages like java forbid this.

If that was supposed to let me sleep tonight it didn't work. :o

that's like storing cherry bombs under the hood of the car, hanging off the ignition wire, counting on the voltage never reaching a point that lights them!

shudder

I do find it odd that C lacks some kind of exit command for this

break;

Or structure your code differently.

Or change the value of whatever you are testing so that when it's tested it's outside the limit.

it was something like

for (i=0,10,i++){
  for (j=0,10,j++{
   do_some_stuff;
   if (all done()) goto done:
  }
}

done:

A break would have only popped me into the outer loop.

In Fortran, I'd simply label the loop beginning, and use it as part of an exit.

I suppose the compiled code would generally be the same in either case, but the exit method just feels "cleaner". :). More importantly, it doesn't leave a table out there dangling after the loop . . .

edit: As more old neurons slowly burn of dust and fire, I think that the bounds for each loop changed with the termination point being saved for the next time. I started a thread back then, and after reading everything, goto was the cleanest solution. But again, it seems to be an every 20 years thing :slight_smile:

How about something like this ?

void setup()
{
  Serial.begin(115200);
  for (int outer = 0; outer < 8; outer++)
  {
    for (int inner = 0; inner < 8; inner++)
    {
      Serial.println(inner);
      if (inner == 4)
      {
        outer = 8;
        break;
      }
    }
    Serial.println();
  }
}

void loop()
{
}