Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate through enums in C?

Tags:

c

enums

let's say I have a couple of hardware registers which are defined within an enum:

typedef enum registers
{
    REG1 = 0,
    REG2 = 1,
    REG3 = 2,
    REG4 = 4,
    REG5 = 6,
    REG6 = 8,
    
    REG_MAX,
    
}

I have default values for these registers(actually not decided in which way to define them, #define, array or enum...):

// This is just conceptual, not really an enum, array or #define :)
typedef enum values
{
    VALUE_REG1 = A,
    VALUE_REG2 = B,
    VALUE_REG3 = C,
    VALUE_REG4 = 53,
    VALUE_REG5 = 88,
    VALUE_REG6 = 24,
    
    MAX_NUM_REG_VALUES    
}

I have a function which can read the hardware registers:

uint8 readRegs(uint8 regaddr);

Now I would like to loop through registers, and on each element call the readRegs() function and compare it to the enum regvalues. It looks like:

registers regs;
reg_values values;
uint8 readfromregs;

for (regs = REG1; regs <= REG_MAX; regs++)
{
    readfromregs = readRegs(regs);
    for (values = VALUE_REG1; reg_values <= MAX_NUM_REG_VALUES; reg_values++)
    {
        if (readfromregs != values)
        {
             // Value not correct
        }
        else
        {
             // value correct
        }
    }
}

This will not work because it's not possible to iterate in enum in this way. Does anybody have a better solution? How to define the construct enum reg_values? The enum registers must be fix (this can not be changes to array).

like image 787
JohnDoe Avatar asked Mar 23 '17 10:03

JohnDoe


People also ask

Can you iterate through an enum in C?

you can iterate the elements like: for(int i=Bar; i<=Last; i++) { ... } Note that this exposes the really-just-an-int nature of a C enum. In particular, you can see that a C enum doesn't really provide type safety, as you can use an int in place of an enum value and vice versa.

Can you iterate through an enum?

To iterate through an enumeration, you can move it into an array using the GetValues method. You could also iterate through an enumeration using a For... Each statement, using the GetNames or GetValues method to extract the string or numeric value.

Can you iterate through an enum class C ++?

Can you loop through an enum C ++? Yes. It iterates over an std::initializer_list<Item>.

What is typedef enum in C?

A typedef is a mechanism for declaring an alternative name for a type. An enumerated type is an integer type with an associated set of symbolic constants representing the valid values of that type.


2 Answers

I'd probably define a struct

struct RegInfo
{
    registers number;
    values defaultValue;
}

Then I would create an array matching the register number to the default value

struct RegInfo registerInfo[] =
{
    { REG1, VALUE_REG1 },
    { REG2, VALUE_REG2 },
    { REG3, VALUE_REG3 },
    { REG4, VALUE_REG4 },
    { REG5, VALUE_REG5 },
    { REG6, VALUE_REG6 },
};

Now to iterate the registers:

for (int i = 0 ; i < sizeof registerInfo / sizeof(RegInfo) ; ++i)
{
    values value = readFromReg( registerInfo[i].number);
    if (value != registerInfo[i].defaultValue)
    {
         // Not the default
    }
}

If you want an inner loop foe each value, same trick except the array can be directly of the values

values allValues[] = { VALUE_REG1, Value_REG2, ... , VALUE_REG6 };

There is a risk that you'll forget to put new values/registers in the relevant array, but that's what unit testing is for.

like image 72
JeremyP Avatar answered Oct 20 '22 01:10

JeremyP


REG_MAX will be 9 and MAX_NUM_REG_VALUES will be 25 so you can't use those. You'll have to enumerate the constants in a different way.

Based on the struct solution in another answer by @JeremyP, you could use a 3rd enum for the actual indices:

typedef enum
{
  REG_INDEX1 = REG1,
  REG_INDEX2 = REG2,
  REG_INDEX3 = REG3,
  REG_INDEX4 = REG4,
  REG_INDEX5 = REG5,
  REG_INDEX6 = REG6,
  REG_INDEX_N // number of registers
} reg_index;

This enables some ways to improve data integrity:

struct RegInfo registerInfo[] =
{
  [REG_INDEX1] = { REG1, VALUE_REG1 },
  [REG_INDEX2] = { REG2, VALUE_REG2 },
  [REG_INDEX3] = { REG3, VALUE_REG3 },
  [REG_INDEX4] = { REG4, VALUE_REG4 },
  [REG_INDEX5] = { REG5, VALUE_REG5 },
  [REG_INDEX6] = { REG6, VALUE_REG6 },
};

_Static_assert(sizeof(registerInfo)/sizeof(*registerInfo) == REG_INDEX_N, 
               "Enum not in sync with array initializer");

This is the solution I would recommend.


If you are very pedantic with data integrity and want to avoid code repetition, there is also some evil macro magic you can use. Namely "X macros" that come at the expense of severely reduced readability:

// whatever A, B and C is supposed to be
#define A 10
#define B 11
#define C 12

#define REG_LIST \
  X(0, A)        \
  X(1, B)        \
  X(2, C)        \
  X(4, 53)       \
  X(6, 88)       \
  X(8, 24)       \

int main (void)
{
  // iterate through the value pairs:
  #define X(reg, val) printf("reg: %d val:%.2d\n", reg, val);
  REG_LIST
  #undef X
}

Similarly, you can use X macros to create the enums:

typedef enum // create REG1, REG2, REG4 etc
{
  #define X(key, val) REG##key = key,
  REG_LIST
  #undef X
} registers;

typedef enum // create VALUE_REG1, VALUE_REG2, VALUE_REG4 etc
{
  #define X(key, val) VALUE_REG##key = val, 
  REG_LIST
  #undef X
} values;
like image 20
Lundin Avatar answered Oct 19 '22 23:10

Lundin