0

I came across this syntax in several macros, in linux kernel 2.4.31 files, but to keep things in focus I am asking regarding __set_task_state(tsk, state_value) macro defined in include/linux/sched.h:

#define __set_task_state(tsk, state_value)      \
    do { (tsk)->state = (state_value); } while (0) 

What is the "advantage" of defining the above macro with the do while(0) statement ? Any side effects ? It's more convenient to define it like so :

#define __set_task_state(tsk, state_value)  (tsk)->state = (state_value) 
Rachid K.
  • 4,490
  • 3
  • 11
  • 30
Adam
  • 2,820
  • 1
  • 13
  • 33
  • 4
    Does this answer your question? [Why use apparently meaningless do-while and if-else statements in macros?](https://stackoverflow.com/questions/154136/why-use-apparently-meaningless-do-while-and-if-else-statements-in-macros) – Peter Nov 23 '20 at 07:46
  • 1
    `__set_task_state(tsk, state) + 1;` is a good example. With `do {} while(0)` that's an error. Without, `tsk->state` is going to be set to some strange value. – KamilCuk Nov 23 '20 at 08:04

3 Answers3

3

There are at least two reasons for defining it using do-while instead of without it.

First, if you define it without do-while then the macro can be used as a function that returns value. If you don't like that you should use do-while wrapper even for single statement macros.

Second reason is similar. If you define it without wrapper then you can use it to assign value: set_task_state(my_task, state) = different_state;

It would be perfectly legal, but very confusing for anyone reading your code. It works because this expands to: (my_task)->state = (state) = different_state;

TCvD
  • 600
  • 2
  • 8
1

Since this macro expands to a single line it doesn't make much sense, but there is a side effect, using the do ... version makes the expression not assignable in both directions:

state = __set_task_state(tsk, state_value); // syntax error in the do version


__set_task_state(tsk, state_value) = state; // syntax error in the do version
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
1

Another reason is for the if-else statements. If you use it like this without the do-while(0) in the macro:

if (check)
  __set_task_state(tsk, state_value);
else
  do_something_else;

The compiler will complain because of a "else" without associated "if" (e.g. orphan "else") as the macro expansion will translate the above code as two instructions under the "if" without braces:

if (check)
  (tsk)->state = (state_value);; <-- The double ";;" separates two instructions under the "if".
                                 <-- This makes the following "else" orphan
                                 <-- This requires to add braces to fix the compilation error
else
  do_something_else;

The do-while(0) in the macro makes the grouped instructions appear as a single instruction. Here the expansion is:

if (check)
  do { (tsk)->state = (state_value); } while(0); <-- Here, there is a single instruction under the "if"
                                                 <-- So the following "else" is syntactically correct
                                                 <-- No need to add braces
else
  do_something_else;

N.B.: This tip is normally used to group several instructions in a macro to make them appear as a single instruction:

#define MACRO() do { inst1; inst2; inst3;...; } while (0)

This makes the following work without the need of braces:

if (check)
  MACRO();
else
  do_something_else;

This also avoid severe hidden bugs like this one for example:

while(check)
  MACRO();

Without the do-while(0) construct, only the first instruction (i.e. inst1) would be executed in the while(check) loop!

In this link, there are additional tips to avoid traps with the c-preprocessor. The above tip is described in §3.

Rachid K.
  • 4,490
  • 3
  • 11
  • 30