switch with enums does not work as expected - where's my error?

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey, using atmel studio 6.1 with win-avr I guess. I've had to replace my switch with a simple if-statement, I can't see the difference.

I've defined an enum beforehand:

enum LED {
	BOARD,
	BREAD
};

static volatile enum LED active = BOARD;

And the switch:

	switch (active) {
		case BOARD:
			active = BREAD;
		default:
			active = BOARD;
	}

This does not work properly. Here's the code that then reads the result from this switch:

uint8_t new = ADCH / 4;
switch (active) {
		case BOARD:
			boardlim = new;
		default:
			breadlim = new;
	}

Anyone knows what's wrong? The code seems to do everything, not just one branch..

 

Thanks

sol i sinne - brun inne

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Visual Basic has a 'select' statement (which is very similar to a C 'switch') but in VB each 'case' has an implicit exit before the next 'case'.

The C (and C++) language does not have such an implicit break, therefore your 'case BOARD:' will fall through into the next 'case' statement.

Sometimes that is useful, but I suspect that you want to do something like ;

switch( acLive)
{
case BOARD:
    acLive = BREAD;
    break;
default:
    acLive = BOARD;
    break;
}

Now, there will be raging arguments as to whether or not the 'break' in the 'default:' condition is necessary/useful/ugly/confusing/etc etc, but the main point is still that in almost all 'case' conditions you will want to 'break' before the next 'case' condition.

 

Last Edited: Sun. Feb 15, 2015 - 11:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

aaah, I forgot. Good. C is really bad for regular programming eh :D in haskell it would be like

x = case active of
        BOARD     ->  BREAD
        otherwise ->  BOARD

so intuitive

Thanks for the help dawg!

sol i sinne - brun inne

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow, that is truly stupendous.. What would be the difference then (if you omit the "break"s) between making a switch and getting a match on the first item, and just writing all the code in a regular expression? Such a strange decision to make.

sol i sinne - brun inne

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Requiring an explicit break after each case allows multiple cases to be grouped together:

switch (day_of_week) {
    case MONDAY:
    case TUESDAY:
    case WEDNESDAY:
    case THURSDAY:
    case FRIDAY:
        week_day_stuff();
        break;
    case SATURDAY:
    case SUNDAY:
        week_end_stuff();
        break;
}

 

It also allows the handling of common cases:

switch (day_of_week) {
    case MONDAY:
        monday_only_stuff();
    case TUESDAY:
    case WEDNESDAY:
    case THURSDAY:
    case FRIDAY:
        week_day_stuff();
        break;
    case SATURDAY:
    case SUNDAY:
        week_end_stuff();
        break;
}

Don't be too critical of a language until you know what it's capable of ;)

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow, that is truly stupendous

 

Well, C more or less was first to the table, so you might say that the other languages got it wrong ;-)

 

In e.g. Pascal and Ada (case statement) and VB (select statement) you are looking at syntactic sugar for a nested if-statement.

 

Think of the switch-statement in e.g. C, C++, C# and Java as syntactic sugar for a "conditional multi-goto" or "jump table". All those languages sport the "case fall-through" feature.

 

I know that C++ wants to be "compatible" with C here, and I speculate that the designers of C# and Java thought along similar paths.

 

C is not at all a language with design goals such as "type safe" or "high level of abstraction". Indeed it has been described as a "macro macro assembler". If you use C with a mindset that it is a highly abstracted/abstracting language you will likely trip in ways similar to the above many times - and the outcome might be disastrous.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Mon. Feb 16, 2015 - 07:53 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To add to the foregoing. I wouldn't do what you are doing anyway. You have (once corrected):

switch( active)
{
case BOARD:
    active = BREAD;
    break;
default:
    active = BOARD;
    break;
}

Surely what you really mean is:

switch( active)
{
case BOARD:
    active = BREAD;
    break;
case BREAD:
    active = BOARD;
    break;
default:
    // some handling for the case we don't ever actually expect to be in
    break;
}

I wouldn't just use "default" as "none of the others so it must be ...". Specifically (as much for the benefit of the maintainer as anything else) include case's for all the expected enum values then, if you deem it necessary use "default" for the "WTF, how on earth did we arrive here?" condition. Perhaps assert() in it or something? If you are in default: it implies either that "active" has been corrupted or that perhaps you have added another enum value and then forgotten to add a handling "case" for it.

Last Edited: Mon. Feb 16, 2015 - 10:09 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:

Requiring an explicit break after each case allows multiple cases to be grouped together

Don't be too critical of a language until you know what it's capable of ;)

I mean, in some cases it might be useful, the question is how many :D  But I don't hate C, if I've learnt anything that is to never take anything for granted when programming in C, that will get you a long way.

 

JohanEkdahl wrote:

C is not at all a language with design goals such as "type safe" or "high level of abstraction". Indeed it has been described as a "macro macro assembler". If you use C with a mindset that it is a highly abstracted/abstracting language you will likely trip in ways similar to the above many times - and the outcome might be disastrous.

Yep, I do view it as some sort of macro assembler. I would like to try assembler again some time though... That's the charm about functional languages like Haskell and Prolog etc, once you pass the typechecking it just works.

 

clawson wrote:

To add to the foregoing. I wouldn't do what you are doing anyway. Surely what you really mean is:

switch( active)
{
case BOARD:
    active = BREAD;
    break;
case BREAD:
    active = BOARD;
    break;
default:
    // some handling for the case we don't ever actually expect to be in
    break;
}

I wouldn't just use "default" as "none of the others so it must be ...". Specifically (as much for the benefit of the maintainer as anything else) include case's for all the expected enum values then, if you deem it necessary use "default" for the "WTF, how on earth did we arrive here?" condition. Perhaps assert() in it or something? If you are in default: it implies either that "active" has been corrupted or that perhaps you have added another enum value and then forgotten to add a handling "case" for it.

You are right, for both readability and safety that solution is better. It's just my built-in (into my brain) optimizer that just realizes this will work if everything is correct anyway :$ premature optimization is the root of all evil as my programming teacher used to say..

sol i sinne - brun inne

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

C is really bad for regular programming eh :D in haskell it would be like

Fifty million Frenchmen can't be wrong.

 

If you don't like C, then don't program in C.  Stick with [Eddie] Haskell, then, but don't expect help from Mr. Cleaver.

 

How would you implement Duff's Device in Haskell?

 

While many of us irregular programmers would do our C switch with 90+% of cases having a break; , it isn't unusual to try to be clever on occasion and have purposeful fall-throughs, and sometimes several in a row to "unroll" certain actions.

 

Before you decried "not working with enum", did you try without the enum?  Bet you'd get the same results...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If one is feeling really confident, the default case could be "__builtin_unreachable();".

It might allow otherwise unavailable optimizations.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

syntactic sugar for a "conditional multi-goto"

"Oh!  Like Fortran's "GOTO (lineno, lineno, lineno), I" statment!  :-)