Search code examples
cenumsc99static-assertmisra

Staticly assert that enum is a certain underlying type


MISRA 10.1 forbids performing arithmetic on an object of an enumerated type.

An operand of essentially enum type should not be used in an arithmetic operation because an enum object uses an implementation-defined integer type. An operation involving an enum object may therefore yield a result with an unexpected type. Note that an enumeration constant from an anonymous enum has essentially signed type.

They also stipulate that the ++ and -- unary operators are treated as binary addition and subtraction for the purpose of this rule.

If I use integers in the loop control structure I'll still need to cast them back to enum later, which will violate Rule 10.5

The value of an expression should not be cast to an inappropriate essential type

Is there a way I can use static assertions to guarantee some assumptions about the underlying enum type? This code may be reused in the future on another architecture. I'd like to deviate from 10.5 in this case with confidence that the code will throw a compile time error if some assumption about what the underlying enumerate type is violated.

Contrived example:

enum {thing1, thing2, ... , thing_max } thing_index_t
...
for(int thing_index = 0; thing_index < (int) thing_max; ++thing_index)
{
    init_something((thing_index_t) thing_index);
    //             ^~~~~~~~~~~~~~~~~~~~~~~~~
    // cannot cast a signed value to an enum type
    // [MISRA 2012 Rule 10.5, advisory]
}

This should always be a safe cast if I statically assert that sizeof(thing_index_t == int); and that thing1 == 0u right?

Int will always be large enough to hold my whole range of values without promotion FWIW.


Solution

  • Rule 10.5 is overall sound, but controlled conversions from enum to signed/unsigned aren't dangerous. MISRAs concern is that you might have an enum like enum {thing1=123, thing2=456, .... But if you know that the enumerator constants are from 0 to max, it is then mostly safe to go to/from integers.

    You don't need a formal deviation for advisory rules. I would rather leave a comment such as

    /*  Violates MISRA 10.5 but iterating from thing1 to thing_num is safe. 
        Integer type is used since arithmetic on enums is forbidden by 10.1. */
    

    (Or use whatever process you have in place for dealing with advisory rules.)


    As for static asserts, sizeof(thing_index_t == int) proves nothing, since the allowed enumeration constant values is what matters. And thing1 == 0u is guaranteed by the C standard so you don't need to assert that.

    A static assert to ensure enum integrity should rather look like

    #define THING_VALUES   \
      thing1,              \
      thing2,              \
      thing_num            \
    
    typedef enum { thing1=123, thing2=456, thing_num } thing_index_t;
    const size_t expected_size = sizeof((thing_index_t[]){ THING_VALUES }) / sizeof(thing_index_t);
    
    _Static_assert( thing_num+1 == expected_size );
    

    where the compound literal (thing_index_t[]){ THING_VALUES }) gets a size corresponding to the number of enumeration constants in the list. expected_size is the number of items. This asserts that no special initializers such as thing1=123 are present.

    The only loop hole is something exotic like thing1=123, thing2=1, which this won't catch. To protect against that, you would need to go even further with macros, implementing the whole thing with X macros etc.