Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C - Using enum for bit flags - warning: enumerated type mixed with another type

I'm passing enumeration constants as bit-flags to a function that expects the enumeration type as input, like this:

// Enumeration type
typedef enum
{
    LED_RED    =        (1 << 0),
    LED_GREEN  =        (1 << 1),
    LED_YELLOW =        (1 << 2),
    LED_ORANGE =        (1 << 3),
} LedType;

...

// Function declaration
void setOnLed(LedType led);

...

// Function call
setOnLed(LED_RED | LED_GREEN | LED_YELLOW);

When I call the function, I get a warning:

warning: #188-D: enumerated type mixed with another type

The warning is because LED_RED | LED_GREEN | LED_YELLOW is converted to an integer and is not a LedType.

I could avoid the warning by adding LED combinations to the LedType enumeration but that means I have to add all possible combinations... and if I add more LED options to the enum, it will become really messy...

I could use arrays as an input to the function but that will require more code when calling the function, I prefer a simple function call to set LED's.

I am programming an ARM based micro-controller (STM32) using Keil µVision IDE.

My question

Is there a simple safe way to avoid this warning or another way to encapsulate all the LED's in one meaningful type/object so I can easily pass them to a function and process them in a loop?


The full story

I'm writing a program for an ARM based MCU which is connected to several LED's. In many places in the program, we will torn on/off, toggle and blink different combinations of the LED's. To make this clean and simple, I want to write several functions that get as input any combination of the LED's and do the same operations on all.

I created a struct named LedConfig with the hardware configurations of a LED and an array of LedConfig that contains the configuration of each LED:

typedef struct
{
    // Hardware configurations of a LED
    ...
} LedConfig;

...

LedConfig LedArry[LEDS_LED_COUNT] = 
{
    [0] = { /* Red LED config    */ }, 
    [1] = { /* Green LED config  */ }, 
    [2] = { /* Yellow LED config */ }, 
    [3] = { /* Orange LED config */ }
};

Now, I would like a simple way to pass several LED's to a function and process them in a loop.

I created a number of bit flags for each LED:

// Number of LED's defined in the system
#define LED_COUNT           4

// LED flags, for usage in LED's function
#define LED_RED             (1 << 0)
#define LED_GREEN           (1 << 1)
#define LED_YELLOW          (1 << 2)
#define LED_ORANGE          (1 << 3)

Defined a function:

void setOnLed(uint32_t led)
{
    uint32_t bitMask = 1;
    for(int i = 0; i < LED_COUNT; i++)
    {
        if(led & bitMask)
        {
            LedConfig* ledConfig = &LedArry[i];
            // Turn on LED ...
        }
        bitMask <<= 1;
    }
}

Now I can pass the LED's to the function with bitwise or operation:

setOnLed(LED_RED | LED_GREEN | LED_YELLOW);

This works fine but...

I would prefer to use an enum instead of defines for LED flags, in order to encapsulate theme in one meaningful type/object.

I replaced the defines with an enumeration:

typedef enum
{
    LED_RED    =        (1 << 0),
    LED_GREEN  =        (1 << 1),
    LED_YELLOW =        (1 << 2),
    LED_ORANGE =        (1 << 3),
} LedType;

And modified setOnLed function input to get the enumeration type:

void setOnLed(LedType led)
{
    // ...
}

When I call the function with several LED's:

setOnLed(LED_RED | LED_GREEN | LED_YELLOW);

I get the warning:

warning: #188-D: enumerated type mixed with another type

Note: uint32_t is from stdint.h and is an unsigned 32-bit integer.

like image 894
Eliahu Aaron Avatar asked Aug 07 '19 10:08

Eliahu Aaron


Video Answer


1 Answers

I would prefer to use an enum instead of defines for LED flags because I prefer to encapsulate theme in one meaningful type/object.

This is perfectly fine but keep two things in mind:

  • The type of an enumeration constant, LED_RED in your case, is always of type int which is signed.
  • The type of an enumerated type, LedType in your case is implementation-defined. The compiler can pick a smaller integer type if the values used would fit inside one.

Generally, you'll want to avoid signed types in embedded systems, because of integer promotion and various hiccups with bitwise operators.

One such hiccup is left-shifting a signed integer constant 1. This is of type int and signed, so on a 32 bit system, 1 << 31 would mean an undefined behavior bug. Therefore, always unsign suffix your integer constants: always use 1u << n instead of 1 << n.

I get the warning: warning: #188-D: enumerated type mixed with another type

Yeah because the function expects a uint32_t but you pass a int, since all operands in the expression LED_RED | LED_GREEN | LED_YELLOW are int - they are enumeration constants as described above. You should rewrite the function to take LedType as parameter instead.

Example:

// led.h

typedef enum
{
  LED_NONE   = 0u,
  LED_RED    = 1u << 0,
  LED_GREEN  = 1u << 1,
  LED_YELLOW = 1u << 2,
  LED_ORANGE = 1u << 3,
  LED_ALL = LED_RED | LED_GREEN | LED_YELLOW | LED_ORANGE;
} led_t;

#define LED_PORT PORTX


void set_led (led_t leds);

// led.c

#include "led.h"

void set_led (led_t leds)
{
  // this assuming you'll want to use the function both to set and clear leds
  uint32_t led_port = LED_PORT;
  led_port &= (uint32_t) ~LED_ALL;
  led_port |= (uint32_t) leds;
  LED_PORT = (uint32_t) leds;
}

The (uint32_t) casts are strictly speaking not necessary but will sate pedantic compilers and MISRA-C checkers.

like image 196
Lundin Avatar answered Oct 17 '22 15:10

Lundin