Use #ifdefs and #define to optionally turn a function call into a comment

30,988

Solution 1

Try this:

#ifdef SOMETHING
#define foo(x)
#else
#define foo(x) MyFunction(x)
#endif

If your function has several arguments, then:

#ifdef SOMETHING
#define foo(x,y,z)
#else
#define foo(x,y,z) MyFunction(x,y,z)
#endif

If your function has a variable number of arguments, then your compiler may support so-called "variadic macros", like this:

#ifdef SOMETHING
#define foo(...)
#else
#define foo(...) MyFunction(__VA_ARGS__)
#endif

The reason which I've seen this kind of thing used in practice is to get rid of logging functions from a release build. However, see also Separate 'debug' and 'release' builds? in which people question whether you should even have different builds.


Alternatively, instead of redefining the function call as nothing, Jonathan's comment to this answer suggested doing something like the following:

#ifdef SOMETHING
#define foo(...) do { if (false) MyFunction(__VA_ARGS__) } while (0)
#else
#define foo(...) do { if (true) MyFunction(__VA_ARGS__) } while (0)
#endif

The reasoning for doing this is so that the function call is always compiled (so it won't be left with gratuitous errors like references to deleted variables), but only called when needed: see Kernighan & Pike The Practice of Programming and also the Goddard Space Flight Center programming standards.

From a debug.h file (originating from 1990, and therefore not using __VA_ARGS__):

/*
** Usage:  TRACE((level, fmt, ...))
** "level" is the debugging level which must be operational for the output
** to appear. "fmt" is a printf format string. "..." is whatever extra
** arguments fmt requires (possibly nothing).
** The non-debug macro means that the code is validated but never called.
** -- See chapter 8 of 'The Practice of Programming', by Kernighan and Pike.
*/
#ifdef DEBUG
#define TRACE(x)    db_print x
#else
#define TRACE(x)    do { if (0) db_print x; } while (0)
#endif /* DEBUG */

With C99, there's no longer a need for the double parentheses trick. New code should not use it unless C89 compatibility is an issue.

Solution 2

Maybe an easier way to do this would be to conditionally omit the body of the function?

void MyFunction() {
#ifndef SOMETHING
    <body of function>
#endif
}

Unless you specifically don't want a function call to be made at all, this seems like a clean way to achieve your goal.

Solution 3

Unfortunately the current C++ version doesn't support variadic macros.

However, you can do this:

#ifdef SOMETHING
#define foo
#else
#define foo(args) MyFunction args
#endif

// you call it with double parens:
foo((a, b, c));

Solution 4

What about something along these lines:

#ifdef NDEBUG
#define DEBUG(STATEMENT) ((void)0)
#else
#define DEBUG(STATEMENT) (STATEMENT)
#endif

You would use it like this to log debugging messages:

DEBUG(puts("compile with -DNDEBUG and I'm gone"));

A non-generic version for formatted output with additional debugging information using C99 variadic macros and the __func__ identifier could look like this:

#ifdef NDEBUG
#define Dprintf(FORMAT, ...) ((void)0)
#define Dputs(MSG) ((void)0)
#else
#define Dprintf(FORMAT, ...) \
    fprintf(stderr, "%s() in %s, line %i: " FORMAT "\n", \
        __func__, __FILE__, __LINE__, __VA_ARGS__)
#define Dputs(MSG) Dprintf("%s", MSG)
#endif

Here's how you'd use these macros:

Dprintf("count = %i", count);
Dputs("checkpoint passed");

Solution 5

Likely, you don't want to do the simple "code removal" as suggested, because your callers will be expecting the side effects of the arguments to happen. Here are some troublesome caller snippets that should get you thinking:

// pre/post increment inside method call:
MyFunction(i++); 

// Function call (with side effects) used as method argument: 
MyFunction( StoreNewUsernameIntoDatabase(username) ); 

If you were to disable MyFunction by simply saying:

#define MyFunction(x) 

then the side effects that the callers were expecting would go away, and their code would break, and be quite difficult to debug. I like the "sizeof" suggestion above, and I also like the suggestion to just disable the body of MyFunction() via #ifdef's, although that means that all callers get the same version of MyFunction(). From your problem statement, I presume that's not actually what you want.

If you really need to disable MyFunction() via preprocessor defines on a per-source-file basis, then I'd do it like this:

#ifdef SOMETHING 
#define MyFunction(x) NoOp_MyFunction(x) 

int NoOp_MyFunction(x) { } 
#endif 

You could even include the implementation of NoOp_MyFunction() inside the source & headers for MyFunction(). You also have the flexibility to add extra logging or debugging information in NoOp_MyFunction() as well.

Share:
30,988
Daniel LeCheminant
Author by

Daniel LeCheminant

Trello Developer @d_lec

Updated on March 31, 2020

Comments

  • Daniel LeCheminant
    Daniel LeCheminant about 4 years

    Is it possible to do something like this

    #ifdef SOMETHING
    #define foo //
    #else
    #define foo MyFunction
    #endif
    

    The idea is that if SOMETHING is defined, then calls to foo(...) become comments (or something that doesn't get evaluated or compiled), otherwise it becomes a call to MyFunction.

    I've seen __noop used, but I don't believe I can use that.

    EDIT(s):

    I don't think I can really use a macro here, because MyFunction takes a variable number of arguments.

    Also, I'd like to make it so the arguments are NOT evaluated! (So doing something like commenting out the body of MyFunction doesn't really give me what I need, as the arguments will still be evaluated)

    • ChrisW
      ChrisW about 15 years
      See whether your compiler support 'variadic macros'.
    • gimpf
      gimpf about 15 years
      Why would you want to do this? Maybe there is some other way to get what you need. Because without context, what you want to do seems rather dangerous...
    • MSN
      MSN about 15 years
      For compilers that don't support variadic macros is it nice to be able to strip out debug strings and function calls. It saves lots of executable space.
    • Daniel LeCheminant
      Daniel LeCheminant about 15 years
      @gimpf Yes, it's to remove trace/debug type statements in release builds.
  • ChrisW
    ChrisW about 15 years
    It will work with a greater-than-one-but-fixed number of arguments. For a variable number of arguments, Google and/or check your compiler documentation for so-called "variadic macros".
  • Daniel LeCheminant
    Daniel LeCheminant about 15 years
    @ChrisW If you modify your answer to use a variadic macro, then I think it's exactly what I need :)
  • David Thornley
    David Thornley about 15 years
    Variadic macros are in the latest C standard. I'm not sure about C++0x, can't get the latest draft to download right now, and they aren't listed in the Wikipedia article, but they'll probably be available.
  • ChrisW
    ChrisW about 15 years
    Well, I think I used them recently in the gcc compiler.
  • MattK
    MattK about 15 years
    True. I think that if you don't want the arguments evaluated, then you may have to go as far as #ifdefing out all the calls. The "#define foo(x)" solution will also evaluate your arguments on most compilers.
  • Daniel LeCheminant
    Daniel LeCheminant about 15 years
    @ChrisW They work for me. If you change your answer to include them, I'll accept it as being correct.
  • Graeme Perrow
    Graeme Perrow about 15 years
    I do this with debug logging functions so that (a) they vanish entirely in production builds, and (b) I can pass in a format string and any number of args.
  • Jonathan Leffler
    Jonathan Leffler about 15 years
    You should normally define the non-function call as something like ((void)0) to avoid compilation problems. Having said that, I'm not coming up with a scenario where it actually matters - outside of an expression. And in an expression, you probably wouldn't use the void cast.
  • MSN
    MSN about 15 years
    See my solution for any number of arguments (not including zero).
  • Christoph
    Christoph about 15 years
    A problem I see is that for each function you want to conditionally execute, a new macro has to be defined. Enclosing the whole function call in a macro will work with any number of functions and is more explicit, ie less likely to cause confusion ('Where the hell did my function call go?')
  • rmeador
    rmeador about 15 years
    I think you should probably declare it inline as well to guarantee it gets optimized away. It should support varargs too, so I think this is probably the best solution.
  • Daniel LeCheminant
    Daniel LeCheminant about 15 years
    @Christoph ChrisW is exactly right; I'm trying to have a conditional logging type function that only gets used on certain builds.
  • Ferruccio
    Ferruccio about 15 years
    It depends on the compiler. I've seen VC++ optimize away functions with code in them when it could determine that they had no side effects. Very frustrating when you're trying to do benchmarks ;-)
  • Jonathan Leffler
    Jonathan Leffler about 15 years
    See: Kernighan & Pike "The Practice of Programming" and also the Goddard Space Flight Center programming standards (software.gsfc.nasa.gov/assetsbytype.cfm?TypeAsset=Standard)‌​.
  • ChrisW
    ChrisW about 15 years
    Thanks Jonathan, I'll add that.
  • Phil Hord
    Phil Hord about 15 years
    Ack! Don't use "if (ConditionalLoggingEnabled) { MyFunction(...); }" without giving serious consideration to the "else". For example, "if (somevar) TRACE(somevar); else TRACE(somevar+1);" gets hopelessly confused by the ambiguous else once "TRACE" expands to an extra if-clause.
  • ChrisW
    ChrisW about 15 years
    @phord I added braces: is that enough to de-confuse?
  • Logan Capaldo
    Logan Capaldo about 15 years
    sizeof has the same problem wrt side effects.
  • Jonathan Leffler
    Jonathan Leffler about 15 years
    @phord: yup, you're right. do { if (conditional) MyFunction(...); } while (0) -- @ChrisW: no, braces aren't enough because the semi-colon confuses.
  • sharptooth
    sharptooth about 15 years
    Not only the arguments are evaluated, but the data for them is included which is not very nice if the function is a logging function and arguments are huge strings disclosing the functioning of your application.
  • Jonathan Leffler
    Jonathan Leffler about 15 years
    That gets intolerably clumsy after the second invocation. Don't go down this route.