L-value issue when using (void *) as a shared data container

Here is the structure used in the program:

struct basic_block { void * aux; /* Many other fields, which are irrelevant. */ }; 

Now:

  • At run time, there are several instances of basic_block .
  • The program works in stages / passes, which are performed one by one.
  • The aux field is intended to store the stages and basic_block specific data during the execution of the stage and is freed by the scene itself (so the next stage can reuse it). That is why it is void * .

My scene uses aux to store the struct , so every time I want to access something, I need to throw:

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

Now, my problem: every time this is done, it is pain, and it is difficult to read when it is part of more complex expressions. My suggested solution: use the macro to execute 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) myself as an L-value (for malloc ), so I use this macro, this is not a good idea:

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

My question is:

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

0
source share
4 answers

You can specify the address of the field in struct a_long_struct_name ** , and then look for it:

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

This will work in both of the designs you showed.

If the feature set for a particular aux type can be known at the point where struct basic_block , it would be cleaner to use a join:

 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) 
+2
source

Try:

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

Note that this introduces aliasing, so you need to be consistent in using the macro.

+2
source

No need to litter the code with macros:

 struct a_long_struct_name* strp = malloc(sizeof *strp); if (!strp) <handle OOM> bb->aux = strp; strp->foo_field = foo; etc. 
+2
source

You can not. C denies access to an object of one type ( void * ) by expressing an lvalue of another type ( struct a_long_struct_name * ), and violation of this rule is a violation of aliases. What this means from a practical point of view is that the compiler is allowed to assume that these two lvalues ​​cannot refer to the same memory and, therefore, can change the reading and writing order so that your code badly violated your code . Formally, this is simply undefined behavior, which means that anything can happen.

What you should do is use the original version of your code in the question and forget about using MY_DATA(bb) as an lvalue. Instead, just assign directly to bb->aux . For this, void * .

+1
source

Source: https://habr.com/ru/post/919974/


All Articles