1

Suppose I have the following construct in multiple places in my code and want to make my code more legible:

#if HAVE_LIBFOOBAR
    foobar_func(data);
#endif

I was thinking of writing a function-style macro around this, which would handle the conditionals, making the occurrences in the code look like a regular function call:

    foobar_func_if_available(data)

If the condition is true, this would be replaced with a call to the actual function, else it would be a no-op.

Thus, something like:

#if HAVE_LIBFOOBAR
#define foobar_func_if_available(x) foobar_func(x)
#else
#define foobar_func_if_available(x) {}
#endif

Questions:

  • Does {} work as a no-op? Is it safe from having unintended effects (such as being used in an unbracketed if statement)? If not, what would I use?
  • Do I have to have two independent #defines wrapped in conditionals, or is there a way to do it the other way round (one #define with the conditionals inside the function-style macro)?

Edit: it has been suggested that this is a duplicate of another question, but in my opinion it is not: the other question asks “what is the problem solved with this construct”, mine is “what construct will solve my problem”. Indeed the other question has a possible solution to my problem, it does not cover all aspects of my question.

user149408
  • 5,385
  • 4
  • 33
  • 69
  • 1
    Possible duplicate of [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) – Raymond Chen Mar 17 '19 at 16:05

3 Answers3

1

You cannot achieve it with a single #define.

You do not need {} as a no-op, you can define an empty expression in a number of ways:

#define foobar_func_if_available(x)
#define foobar_func_if_available(x) ;
#define foobar_func_if_available(x) do{}while(0)

There are circumstances where either of these may cause syntactic issues, but for void functions neither is likely to cause a problem - the solution breaks down for non-void functions however.

A better solution avoiding function-like macros altogether is to define the function body conditionally:

void foobar_func( int n )
{
    #if defined HAVE_LIBFOOBAR
       // do something
    #else
       // do nothing
    #endif
}

whether the empty function results in no code is a matter for the compiler and the optimisation level applied, but importantly the code will work syntactically in all situations where a call to foobar_func() is valid. To worry about it being a no-op or not is probably sweating the small stuff.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • 2
    This will cause problems if somebody forgets a semicolon: `foobar_func_if_available(x) something_else();` -- somebody who doesn't have LIBFOOBAR will see this compile successfully, but somebody who has it will see a compiler error. The traditional solution is a `do{}while(0)` with no trailing semicolon. – Raymond Chen Mar 17 '19 at 16:04
  • @RaymondChen : For multi-statement.function-like macros your advice holds, but for a single function call alias using a do-while would you prevent the use of the macro in an expression. However I have strictly answers the question regarding the NO-OP macro, the other definition is not in question and is not my code. – Clifford Mar 17 '19 at 16:10
  • 1
    Leaving it empty would also prevent its use in an expression. – melpomene Mar 17 '19 at 16:12
  • @wildplasser : alternative solutions to mine should be posted as answers not comments. I am aware of the deficiencies of this solution, and welcome better suggestions. – Clifford Mar 17 '19 at 16:17
  • True, best would be for the "no-LIBFOOBAR" case to specify a literal value of the same type as the return value of `foobar_func`. – Raymond Chen Mar 17 '19 at 16:17
  • @Clifford : Sorry.I was confused. – wildplasser Mar 17 '19 at 16:19
  • "*The `{ }` solution would result in syntactically invalid code*" - No, it would not (most of the time). – melpomene Mar 17 '19 at 16:25
  • @RaymondChen : It was a terrible answer in any case - rewitten. I have use the technique but only for debug code insertion that would make no sense in an expression. – Clifford Mar 17 '19 at 16:26
  • @melpomene : True - anywhere a void function makes sense. It is not a very general solution for functions that return a value. – Clifford Mar 17 '19 at 16:27
  • 1
    I think the empty semicolon is bad -- it creates two empty statements if somebody writes `foobar_func_if_available(x);`. That messes up unbraced if-elses. – Peter - Reinstate Monica Mar 17 '19 at 16:41
  • The `{}` notation doesn't work well in contexts such as `for (i = 10; --i > 0; foobar_func_if_available(i + 37)) { … }` — and neither does the `do { } while (0)` notation. Using nothing, or `0` works better. Or you can ensure that the compiler inspects the arguments for validity by using `#define foobar_func_if_available(data) ((void)(data), 0)`; that expands the arguments and ignores them, but the result expression is 0 which can be converted to pointer or numeric value as required, or ignored. – Jonathan Leffler Mar 17 '19 at 16:58
  • @JonathanLeffler : I don't disagree - every macro solution I offered attracted comments regarding deficiencies. I assumed that id `{}` is valid then teh function is `void` so would not be used like that as mentioned in my answer, it is not a general solution. Your solution is certainly better. I leave you to propose it or leave it in the comment - I am not confident I'd get it right if I added it to my answer. All macro solutions are bets avoided IMO. – Clifford Mar 17 '19 at 17:10
  • @Jonathan it depends on whether the function returns a value, doesn't it? Generally (for void funcs) do..while is the canonical form because it replaces "true" function calls properly in all cases (in particular, does not create spurious empty statements). – Peter - Reinstate Monica Mar 17 '19 at 20:36
  • @JonathanLeffler Whether do..while is good. If the function returns a value it's probably better to substitute one. – Peter - Reinstate Monica Mar 17 '19 at 20:39
  • @PeterA.Schneider: I showed an example where the `do { … } while (0)` notation does not work — when the function is in the (first or) third term in a `for` loop control. It doesn't matter whether it returns `void` or some value; the `do { … } while (0)` isn't going to work but an expression does work. – Jonathan Leffler Mar 17 '19 at 20:40
  • @JonathanLeffler Oh, I thought those were cases where a value is expected. I see. Interesting, first time I have seen that. – Peter - Reinstate Monica Mar 17 '19 at 20:41
1

Create a dummy function and make the #define point at it (conditionally):


#if HAVE_LIBFOOBAR
  #define foobar_func_if_available(x) foobar_func(x)
#else
  int dummy(int ignored)
  {
  return 0;
  }
  #define foobar_func_if_available(x) dummy(x)
#endif

Or just :

#define foobar_func_if_available(x) 0
wildplasser
  • 43,142
  • 8
  • 66
  • 109
  • In that case you could just do `int foobar_func(int u) { return 0; }` and skip the macro. – melpomene Mar 17 '19 at 16:20
  • In fact my question was simplified: even with `HAVE_LIBFOOBAR` being false, the function might be available, I just do not want my code to use it. While skipping the macro would generally work, in my case it would not. – user149408 Mar 17 '19 at 17:46
  • `((int)0)` would be a little safer. `0` is `((int)0)` but it's also the null pointer constant and an integer constant, which inadvertently greatly expands its uses over `((int)0)`. – Petr Skocik Mar 17 '19 at 17:50
  • The idea is to emulate the signature of the original function (which was not shown) Thus, for `sin(x)` you could produce or return 0.0. – wildplasser Mar 17 '19 at 17:53
  • @wildplasser I understand. I'm just pointing out that if you want to emulate a function returning `int`, it's better done with `((int)0)` rather than `0`, as `0` can be used in many contexts where a non-const int expression couldn't be used. – Petr Skocik Mar 17 '19 at 18:01
1

Macros like ((int)0) or ((void)0) are probably the most flexible/safest no-op macros. They're flexible because you can use them in expressions (unlike do{}while(0)) and they don't break if-else like {} or ; would.

Example of how {} (or ;) macros break if-else:

#define foo() {}
if(1) foo(); else bar(); //syntax error because if(1) {}; else bar(); was pasted

If the macro should emulate an integer returning function, it's better to use a casted integer literal over a plain integer constant as integer constants (and especially zeros) are usable in more contexts (case labels, bitfield sizes, array sizes , null pointer constants) than non-const integer expressions.

You don't need to have two macros as in:

#if HAVE_LIBFOOBAR
    #define foobar_func_if_available(x) foobar_func(x)
#else
    #define foobar_func_if_available(x) ((void)0) /*if foobar_func returns void*/
#endif

You can put the condition inside the macro:

#define foobar_func_if_available(x) \
    (HAVE_LIBFOOBAR?foobar_func(x):((void)0))

Even a very dumb compiler should be able to optimize the constant conditional out.

But if you rely on an empty HAVE_LIBFOOBAR evaluating to 0 inside an #if, then the above won't work -- HAVE_LIBFOOBAR will need to be an integer.

( You could do

#if !HAVE_LIBFOOBAR
    #undef HAVE_LIBFOOBAR
    #define HAVE_LIBFOOBAR 0
#endif
#define foobar_func_if_available(x) \
        (HAVE_LIBFOOBAR?foobar_func(x):((void)0))

to normalize an empty HAVE_LIBFOOBAR into 0 but unless you will reuse the now assured HAVE_LIBFOOBAR's definedness, it seems like an unnecessary complication over the original two foobar_func_if_available macros. )

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142