Mark A.
Mark A.

Reputation: 601

Can macros mimic void returning functions by casting to void?

So... I have macros, that has "SET" functionality and shall mimic a function, that would return void. I could use a do {...} while(0) construct to implement this behavior, but is it legal, and if so is it implementation defined behavior to cast something to void to implement this? (according the very words of the different versions of the C standards, most important for me: C89 still)

More specific: Are the following functions/macros are behaving similar (including being legal and provoking compile time errors when used as rvalue)?

  void setFunction(int * const pVar, const int intvalue) /* as function */ 
  {
        *pVar = intvalue;
        return;
  }

  #define SET_MACRO_DW(pVar,intvalue) \
          do { \
               *(pVar) = (intvalue); \
             } while (0)

  #define SET_MACRO_C2V(pVar,intvalue) \
          ((void) (*(pVar) = (intvalue)))


  void test()
  {
       int target;
       int value = 17;
       int retDummy;

       setFunction(&target, value); /* ok */
       SET_MACRO_DW(&target, value); /* ok */
       SET_MACRO_C2V(&target, value); /* ok? */

       retDummy = setFunction(&target, value); /* CT error/warning */
       retDummy = SET_MACRO_DW(&target, value); /* CT syntax error */
       retDummy = SET_MACRO_C2V(&target, value); /* ??? */
       return;

  }

Upvotes: 2

Views: 675

Answers (1)

Armali
Armali

Reputation: 19395

Are the following functions/macros are behaving similar (including being legal and provoking compile time errors when used as rvalue)?

They are behaving similar when used correctly (i. e. not in an assignment) and probably, but not necessarily, when used as an rvalue (see below).

They are of course legal only when used correctly.

Concerning compile time errors, the Standards from C89 to C11 have very similar wording in section Environment / Conceptual models / Translation environment / Diagnostics as in C89 (bold text added in C99):

A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) for every translation unit that contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined. Diagnostic messages need not be produced in other circumstances.

Now, when using your function/macros as an rvalue, we have two cases.

  1. SET_MACRO_DW()
    The do while statement it expands to is in an assignment expression clearly a violation of the syntax rule assignment-expression, hence a diagnostic message has to be produced.

  2. setFunction() and SET_MACRO_C2V()
    These are void expressions. As an rvalue they don't violate any syntax rule, so the question is whether there is a violation of any constraint. The following wording in section Language / Conversions / Other operands / void didn't change from C89 to C11.

    The (nonexistent) value of a void expression (an expression that has type void) shall not be used in any way, and implicit or explicit conversions (except to void) shall not be applied to such an expression.

    Also, the Standards have very similar wording as

    In this Standard, "shall" is to be interpreted as a requirement on an implementation or on a program; conversely, "shall not" is to be interpreted as a prohibition.

    If a "shall" or "shall not" requirement that appears outside of a constraint is violated, the behavior is undefined.

    Now, I couldn't decide whether the above requirement for a void expression constitutes a constraint, so I asked What are the Constraints in Standard C? The conclusion is that the above is not a constraint in the sense of the standard, hence on the usage as an rvalue an error message is not guaranteed (albeit one should be produced by every sane compiler) and the use results in undefined behavior. But there's still the constraint in section Language / Expressions / Assignment operators / Simple assignment:

    One of the following shall hold: …

    where none of the following holds, hence we have a constraint violated and an error message is guaranteed even though the behavior is explicitly undefined.

So, SET_MACRO_DW() is the most plain and fool-proof construct of the three you presented.

Upvotes: 1

Related Questions