Home > Mobile >  Volatile in struct causing warning in macro
Volatile in struct causing warning in macro

Time:01-31

I use the following type:

/* double_buffer.h */

typedef struct
{
    uint8_t * active_buffer_p;       //< Address of active buffer
    uint8_t current_writing_index;   //< Current writing index in active buffer
    uint8_t buffer_1[BUFFER_SIZE];   //< First buffer
    uint8_t buffer_2[BUFFER_SIZE];   //< Second buffer
} double_buffer_t;


#define DoubleBuffer_init(buffers) do {                 \
        (buffers).active_buffer_p = (buffers).buffer_1; \
        (buffers).current_writing_index = 0;            \
        } while(0)

In my code, I declare an array of double buffer, using the volatile keywoard (because the buffers can be updated/read asynchronously in interrupts and in functions):

static volatile double_buffer_t m_double_buffers[NB_DOUBLE_BUFFERS];

I then initialize those buffers individually:

DoubleBuffer_init(m_double_buffers[id]);

When I compile the software (gcc), I got the following warning:

 error: assignment discards 'volatile' qualifier from pointer target type [-Werror=discarded-qualifiers]
   28 |         (buffers).active_buffer_p = (buffers).buffer_1; \

The reason why I have this warning is quite unclear to me, and I am not sure how to fix it.

Any help would be appreciated (I can update the question if something is not clear).

CodePudding user response:

You get this warning because you have a volatile object, and you create a non-volatile pointer to it.

This is bad as the compiler could access the volatile object without knowing that it is volatile. E.g. it could transform two reads into a single, it could change the order etc.

One way to fix it is to define active_buffer_p to uint8_t volatile * active_buffer_p.

CodePudding user response:

When you declare a struct variable as volatile, each member object gets volatile qualified, as if you had written the struct like this:

typedef struct
{
    uint8_t* volatile active_buffer_p; 
    volatile uint8_t current_writing_index;
    volatile uint8_t buffer_1[10];
    volatile uint8_t buffer_2[10];
} double_buffer_t;

That is, in case of a pointer member, the pointer itself turns volatile. type* volatile = the pointer's address may change at any moment. And not what it is supposed to point at. volatile type* = the pointed-at data might change at any moment.

And therefore when you assign, buffer_1 "array decays" into volatile uint8_t* and then you try to assign that to a pointer qualified as uint8_t* volatile. The pointer types aren't compatible since they have different qualifiers.

The solution is to declare the pointer member volatile uint8_t* active_buffer_p;. Then if the struct variable is declared volatile, this becomes volatile uint8_t* volatile (the pointer and what it points at may change at any moment). And we can always assign a "non qualified" pointer to one with more qualifiers, but not the other way around.

const works exactly the same way.


As a side note, that init macro is just ugly and fills no purpose but obfuscation. Consider dropping it in favour of readable code:

static volatile double_buffer_t m_double_buffers[NB_DOUBLE_BUFFERS] =
{
  [0] = { .active_buffer_p = m_double_buffers[0].buffer_1, 
          .current_writing_index = 0 },
  [1] = { .active_buffer_p = m_double_buffers[1].buffer_1, 
          .current_writing_index = 0 },
  [2] = { .active_buffer_p = m_double_buffers[2].buffer_1, 
          .current_writing_index = 0 },
};

Or 100% equivalent:

static volatile double_buffer_t m_double_buffers[NB_DOUBLE_BUFFERS] =
{
  [0] = { .active_buffer_p = m_double_buffers[0].buffer_1, },
  [1] = { .active_buffer_p = m_double_buffers[1].buffer_1, },
  [2] = { .active_buffer_p = m_double_buffers[2].buffer_1, },
};
  • Related