What does this enum do?

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

Greetings -

 

I am trying to "decode" an MBED program in c++ (though I am not sure it makes a difference in this case). One header file contains this:

typedef enum
{
    LOWPOWER = 0,
    IDLE,
    
    RX,
    RX_TIMEOUT,
    RX_ERROR,
    
    TX,
    TX_TIMEOUT,
    
    CAD,
    CAD_DONE
}AppStates_t;

It appears to be state numbers for a state machine. Does it simply assign a unique number for each of the named states? I've never seen the initializer on the first line, though. What does a typedef do that a "bare" enum does not?

 

Thanks

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

It assigns a sequential number to each item. the typedef makes it a type, so you can use it like an int or char. This allows the compiler to check that if your variable is a AppStates_t, that you only use the enum'd values.

The initialiser defaults to 0, but many of us just put the 0 there to make us feel warm and comfortable. Jim, I think it's time you read up on the C language!

 

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

Yes, I realize that I was just lazy. It looked really unfamiliar and I thought it was something unique to C++.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Any, all, or none of the items in an enum can be "initialised".

 

In the absence of an "initialiser", the previous value +1 is used.

 

Note that "initialisation" is not the right term here, as that implies an initial value which might change - but enums are compile-time constants.

 

Here's a good free online 'C' textbook: http://publications.gbdirect.co....

 

And the chapter on enum: http://publications.gbdirect.co....

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

These days I tend to use cplusplus.com as my reference for things C or C++:

 

http://www.cplusplus.com/doc/tut...

 

As Russell says the =0 in the above is the default anyway but sometimes you might want to "offset" the base of the list so, for example:

typedef enum {
    DIGIT_0 = 0x30,
    DIGIT_1,
    DIGIT_2,
    DIGIT_3,
    etc.
} digits_e;

Now DIGIT_2 == 0x32 and so on.

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

clawson wrote:
These days I tend to use cplusplus.com as my reference for things C or C++:

Likewise: http://blog.antronics.co.uk/2011...

 

sometimes you might want to "offset" the base of the list

Or have gaps in the list

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Mon. Jan 9, 2017 - 10:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Regarding the 'typedef', it allows you to use such variable declaration in C:

 

AppStates_t v;

 

Without the 'typedef' you would write:

 

enum AppStates_e
{
    LOWPOWER = 0,
    IDLE,
    
    RX,
    RX_TIMEOUT,
    RX_ERROR,
    
    TX,
    TX_TIMEOUT,
    
    CAD,
    CAD_DONE
};

enum AppStates_e v;

 

This is the only difference. Presence or absence of the 'typedef' doesn't affect behavior of the 'v' variable at compile and run time.

In C++ 'typedef' is not necessary for enums, although you still can use it.

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

Without use of enum how do you make names for the case's in a switch? (or do you just use magic numbers)  

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

sparrow2 wrote:
Without use of enum how do you make names for the case's in a switch? (or do you just use magic numbers)
Well there's always the option of:

#define IDLE 17
#define WAITING 23
#define OPERATING 51

int state = IDLE;

switch (state) {
    case IDLE: stuff1(); state = WAITING; break;
    case WAITING: stuff2(); state = OPERATING; break;
    case OPERATING: stuff3(); state = IDLE; break;
}

But I think most would agree that:

typedef enum {
  IDLE = 17,
  WAITING = 23,
  OPERATING = 51
} state_e;

state_e state = IDLE;

etc.

is the "better" solution as "state" can now be type checked that only state_e states are being assigned.

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

clawson wrote:
is the "better" solution as "state" can now be type checked that only state_e states are being assigned.

"Better" is in the eye of the beholder, just like beauty.

 

Depending on the toolchain and the stage of the moon and compile options and perhaps more [such as the density of the jump list], it could be that a forced 8-bit operation on the switch variable ends up with 98-bit tests for each case rather than 16.  In a full Mega48 app with a few dozen cases, that can add up.

 

 

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

I said "better" in terms of it being a type so the compiler (or perhaps static analysis tools) can check for type.

 

In my #define example I could use:

int state = 12345;

(which is not a valid state) and I would receive no warning. While with:

state_e state = 12345;

I'd hope to get some warning that "12345 is not a member of state_e" (or words to that effect).

 

I can't remember but I don't think C would be gracious enough to give me such a warning but either C++ or any decent code analyser (lint, splint, cppcheck, Klockwork, etc) would be.

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

clawson wrote:
I can't remember but I don't think C would be gracious enough to give me such a warning

No, it won't.

 

From the online textbook I cited earlier,

6.5. Enums

These fall into the category of ‘half baked’. They aren't proper enumerated types, as in Pascal, and only (sic?) really serve to help you reduce the number of #define statements in your program. 

But they do have the added benefit of proper scoping - as mentioned later on that page.

 

A further benefit (not mentioned in that book)  is that some compilers can generate debug info for them:  so you can see the symbolic names - not just the "magic numbers" - for them in the debugger.

GCC & Atmel Studio do support this.

smiley

 

Most compilers have some option(s) to limit enums to 8 bits, and should still be able to generate code just as efficiently as if "magic numbers" (whether or not via #defines) were used.

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yup, so...

$ cat test.cpp
#include "stdio.h"

typedef enum {
  IDLE = 17,
  WAITING = 23,
  OPERATING = 51
} state_e;

state_e state = 12345;

int main(void) {
    return 0;
}
$ g++ test.cpp -o test
test.cpp:9:17: error: invalid conversion from ‘int’ to ‘state_e’ [-fpermissive]

but:

$ cat test.c
#include "stdio.h"

typedef enum {
  IDLE = 17,
  WAITING = 23,
  OPERATING = 51
} state_e;

state_e state = 12345;

int main(void) {
    return 0;
}
$ gcc test.c -o test -Wall
$ 

So C++ gives me a warning about this but C permits it without mention of any problem even using -Wall. As I say I would hope that lint/splint/etc would catch this kind of thing even for C.

 

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

I don't understand where Lee is coming from.

 

Any type checking would be by the Compiler or Static Analysis tool.   

I don't think that any compiled language does type checking at runtime.

 

David.

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

Of course if I want to be bloody minded I could do:

state_e state = (state_e)12345;

in the C++ and then:

$ g++ test.cpp -o test
$

(no warning/error) but I'm not sure what would motivate me to do this.

 

I think C++'s stronger type checking is reason alone to switch all your source from C to C++ even if you are just going to continue to use the C subset of functionality.

 

(however I do remember when I first changed a raft of .c files to .cpp so they could ultimately make use of some additional C++ classes and it produced a raft of this kind of "invalid conversion" type of error! At the time it just seemed frustrating).

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

Thanks for this very useful discussion. And, the references. 

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

theusch wrote:

clawson wrote:
is the "better" solution as "state" can now be type checked that only state_e states are being assigned.

"Better" is in the eye of the beholder, just like beauty.

 

Depending on the toolchain and the stage of the moon and compile options and perhaps more [such as the density of the jump list], it could be that a forced 8-bit operation on the switch variable ends up with 98-bit tests for each case rather than 16.  In a full Mega48 app with a few dozen cases, that can add up.

 

I don't get that, an enum is an "int", and so is a #define literal value. The compiler will treat them the same way.

 

I agree beauty is subjective, but clearly using an enum to group related constants is better software practice.

Bob. Engineer and trainee Rocket Scientist.

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

david.prentice wrote:
I don't think that any compiled language does type checking at runtime.

But there certainly are languages that do strict type checking at compile time.

 

'C' is not one of them - hence (sometimes) known as a weakly-typed language.

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guess nobody will give enums numbers unless there is a reason!

So I don't see why that should make any difference in the compiled code, (other than the compiler up front know that the numbers are on line)

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

donotdespisethesnake wrote:
I don't get that, an enum is an "int", and so is a #define literal value. The compiler will treat them the same way.

Not in C++:

typedef enum {
  IDLE = 17,
  WAITING = 23,
  OPERATING = 51
} state_e;
state_e state = 12345;
test.cpp:9:17: error: invalid conversion from ‘int’ to ‘state_e’ [-fpermissive]

The compiler "knows" that 12345 is an "int" not a member of "state_e" and is therefore not a valid value to assign to a variable of that type. In fact even if I use:

typedef enum {
  IDLE = 12345,
  WAITING = 23,
  OPERATING = 51
} state_e;

state_e state = 12345;

it's still:

test.cpp:9:17: error: invalid conversion from ‘int’ to ‘state_e’ [-fpermissive]

even though:

state_e state = IDLE;

is going to set it to the same value.

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

david.prentice wrote:
I don't understand where Lee is coming from.

I was just saying that 8-bit operations may be more efficient.  [I often use switch() for  "states" or options similar to the examples used.  It would be rare to have more than 255 states/options.]  A given toolchain may or may not have options for 8-bit switch/case operation; it may or may not have options for 8-bit enums.  IME that can matter in a true microcontroller app.

 

clawson wrote:
So C++ gives me a warning about this but C permits it without mention of any problem even using -Wall.

;)  Again, apparently toolchain-dependent:

 

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

awneil wrote:
ou can see the symbolic names - not just the "magic numbers" - for them in the debugger.

Aside:  I do a bit of C# work.  There is an enum flavour that is a bit mask, and the overloaded print shows the names.

 

        // Used to tell which record types were seen when processing a
        // config file (bit mask for each record type).
        [Flags]
        public enum RecordTypeEnum
        {
            None			= 0x0000,
            Globals			= 0x0001,
            Cips			= 0x0002,
            Modules			= 0x0004,
            Inputs			= 0x0008,
            Outputs			= 0x0010,
            Resources		= 0x0020,
            ManualCycles	= 0x0040,
            Names			= 0x0080,
            Names2			= 0x0100,
            Seqs			= 0x0200,
            Steps1			= 0x0400,
            Steps2			= 0x0800,
            Products		= 0x1000,
            Presets			= 0x2000,
            Sys				= 0x4000,
            AuxMC           = 0x8000    // MC aux presets (link) and area (push amount)
        };

...
// Keeps track of the record types seen when processing a config file.
        protected RecordTypeEnum recordTypesSeen = RecordTypeEnum.None;
...
                                // Update record types seen.
                                recordTypesSeen |= RecordTypeEnum.Cips;
  ...
            // Append to the report.
            WriteRichText (
                "Records Found: " +
                recordTypesSeen +
                Program.CR_LF_STR +
                Program.CR_LF_STR
                );

Which results in:

Records Found: Globals, Cips, Modules, Inputs, Outputs, Resources, ManualCycles, Names

 

So you get the type checking, as well as an "application aid".

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.

Last Edited: Mon. Jan 9, 2017 - 05:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

ICC also produces the error!

 

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

 

But that looks like its truncating to 8 bits?

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

clawson wrote:
But that looks like its truncating to 8 bits?

Indeed.  I have to look at my test CV project again to see if "8-bit enums" is selected...

 

Yes, it was, so CV didn't catch the overflow per se -- but did pick up that it was "another type".

 

Same warning when 8-bie enum disabled.

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

The gnu compilers have had the -fshort-enums option for a long time.  This causes the compiler to use the shortest int that can contain all the elements.  In my code that is almost always 8 bits.

 

The latest C++ compilers allow you to specify the size.  I don't know about the C compilers.

   enum   Events : unsigned char  {
      None = 0,
      };

 

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

I have never seen a C compiler for a 8 bit micro that don't have 8 bit enum (I actually think I have used some where it only could be 8 bit (unsigned char) ).

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

A compiler may have an option to use 8 bit for enums but that's not what the C standard says:

 

So, for example:

$ cat test17.c
#include <stdio.h>

enum {
    ONE = 1,
    TWO
} foo;

int main(void) {
    printf("size enum = %ld, size int = %ld\n", sizeof(foo), sizeof(int));
    return 0;
}
$ gcc -Os test17.c -o test
~$ ./test
size enum = 4, size int = 4

but one can use:

$ gcc -Os -fshort-enums test17.c -o test
$ ./test
size enum = 1, size int = 4

 

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

But because it's a const, there can't be many surprises about the used values, and then even if the compiler use 16 bit int, it will be legal to handle this as 8 bit if it don't change anything.  

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

Well you say it's const but consider something like:

typedef enum {
    STATE1 = 15,
    STATE2
} my_e;

my_e states[5];

How much storage should the compiler assign for that array? I suppose the argument goes that because the elements of the enum (15, 16) will fit in 8 bits it should only be 5 bytes and not 5 * sizeof(int) ? Presumably if I used:

typedef enum {
    STATE1 = 12345,
    STATE2
} my_e;

then it now becomes 10 bytes?

 

EDIT: Yup, so that is what happens:

$ # enum is 15, 16
$ gcc -Os test17.c -o test
$ ./test
size enum array = 20
$ gcc -Os -fshort-enums test17.c -o test
$ ./test
size enum array = 5
$ # now edit it to be 12345,12346
$ gcc -Os -fshort-enums test17.c -o test
$ ./test
size enum array = 10

 

Last Edited: Wed. Jan 11, 2017 - 11:19 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yes but that is because:

-fshort-enums

use the smallest type for enum.

But from your numbers it looks like you use a 32 micro not a 8 bit AVR!

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

Yup, my PC. No point fiddling about with an AVR when you just want to check some generic C concept.

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

It's just because the AVR setup I have here (studio 4.18 and WinAVR 20100110) default to short enum's, for a new project.

 

Your PC don't care , it could even be that 32bit was faster than 8 bit!

Last Edited: Wed. Jan 11, 2017 - 01:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
A compiler may have an option to use 8 bit for enums but that's not what the C standard says:

Didja look a hair further?

 

4 Each enumerated type shall be compatible with char, a signed integer type, or an
unsigned integer type. The choice of type is implementation-defined,110) but shall be
capable of representing the values of all the members of the enumeration.

 

§6.7.2.2 in my version under

 

WG14/N1256 Committee Draft — Septermber 7, 2007 ISO/IEC 9899:TC3
6.7.2.2 Enumeration specifiers
 

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

what I seem to have missed in the discussion is that if you have a longer enum list that you then get a warning if a enum has not been used inside a switch. except if you use the default statement in a switch.

can be very handy if you have multiple switch statements were you use that enum and need to add a new item to the enum list.

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

But it don't know that at compile time.

For a enum of 20 elements one module could use 12 and use default for the rest.

And an other module could use others.

And together all 20 was used!

 

An other problem is if you use ++ for next enum element.