Search code examples
ccastingmacrosvoid-pointerslvalue

L-value trouble when using a (void *) as a generic data container


Here is a structure used in a program:

struct basic_block
{
  void * aux;

  /* Many other fields,
  which are irrelevant.  */
};

Now:

  1. Several instances of basic_block exist during the execution of the program.
  2. The program works in stages/passes, which are executed one after another.
  3. The aux field is meant for storing stage and basic_block specific data during the execution of a stage, and freed by the stage itself (so the next stage can reuse it). This is why it is a void *.

My stage uses aux to store a struct, so each time I want to access something, I have to do a cast:

( (struct a_long_struct_name *) (bb->aux))->foo_field = foo;

Now, my problem: Casting it each time like this is a pain, and difficult to read when it is part of more complicated expressions. My proposed solution was: Use a macro to do the cast for me:

#define MY_DATA(bb) \
  ( (struct a_long_struct_name *) (bb)->aux)

Then I can access my data with:

MY_DATA(bb)->foo_field = foo;

But: I cannot use MY_DATA(bb) itself as an L-value (for a malloc), so I using that macro isn't such a good idea after all:

/* WRONG! I cannot assign to the result of a cast:  */
MY_DATA(bb) = malloc (sizeof (struct a_long_struct_name));

My question:

What can I do in order to refer to aux in a clean way and still be able to use it as an L-value.


Solution

  • You can cast the address of the field to struct a_long_struct_name ** and then dereference it:

    #define MY_DATA(bb) \
       (* ((struct a_long_struct_name **) &(bb)->aux) )
    

    This will work in both the constructs you have shown.

    If the set of possibilities for the concrete type of aux can be known at the point where struct basic_block is declared, though, it would be cleaner to use a union:

    struct basic_block
    {
      union {
          struct a_long_struct_name *alsn;
          /* etc */
      } aux;
    
      /* Many other fields,
         which are irrelevant.  */
    };
    

    and then

    #define MY_DATA(bb) ((bb)->aux.alsn)