简体   繁体   中英

Can `#ifdef` be used inside a macro?

I only found this related question , which isn't quite what I am looking for.

I used to have macros defined inside an #ifdef statement:

#ifdef DEBUG
#   define PRINT_IF_DEBUGGING(format) printf(format);
#   define PRINTF_IF_DEBUGGING(format, ...) printf(format, __VA_ARGS__);
#else
#   define PRINT_IF_DEBUGGING(...)
#   define PRINTF_IF_DEBUGGING(...)
#endif

Now, I want to do the inverse, to have the #ifdef statements inside the macros. Something like this:

#define PRINT_IF_DEBUGGING(format, ...) \
#if defined(DEBUG) print(format); #endif
#define PRINTF_IF_DEBUGGING(format, ...) \
#if defined(DEBUG) printf(format, __VA_ARGS__); #endif

However, I am having an issue using __VA_ARGS__ inside the #ifdef defined .

error: '#' is not followed by a macro parameter
 #define PRINT_IF_DEBUGGING(format, ...)
error: '#' is not followed by a macro parameter
 #define PRINTF_IF_DEBUGGING(format, ...)
warning: __VA_ARGS__ can only appear in the expansion of a C++11 variadic macro
 #if defined(DEBUG) printf(format, __VA_ARGS__); #endif

Is this possible?

This should really be a comment, but I can't format that in a way that will allow me to say what I want to say, so I'm answering instead.

Anyway, just change this:

#if defined(DEBUG) print(format); #endif

to this:

#if defined(DEBUG)
    print(format);
#endif

and so on, and that should fix it.

You can't use #ifdef inside of #define , so no, this is not possible. The first code you showed is the correct solution.

Using #ifdef inside of #define is not possible. But there are still ways you can detect wether or not a macro has been defined within a macro definition.

1. Solution

godbolt

#define CAT(a, b) CAT_IMPL(a, b)
#define CAT_IMPL(a, b) a ## b

#define IS_DEBUG_DEFINED() CHECK((CAT(CHECK_,DEBUG), 0, 1))
#define CHECK_DEBUG ~,~
#define CHECK(tup) CHECK_IMPL tup
#define CHECK_IMPL(a, b, c, ...) c

#define IIF(condition, true_value, false_value) CAT(IIF_,condition)(true_value, false_value)
#define IIF_0(true_value, false_value) false_value
#define IIF_1(true_value, false_value) true_value 

#define PRINT_IF_DEBUGGING(format) IIF(IS_DEBUG_DEFINED(), PRINT_DEBUGGING, PRINT_NOT_DEBUGGING)(format)
// this will be used if DEBUG is defined:
#define PRINT_DEBUGGING(format) debugPrint(format)
// this will be used if DEBUG is NOT defined:
#define PRINT_NOT_DEBUGGING(format) print(format)

PRINT_IF_DEBUGGING(foo) will expand to:

  • if DEBUG is defined: debugPrint(foo)
  • if DEBUG is not defined: print(foo)

Example:

PRINT_IF_DEBUGGING("test1");

#define DEBUG
PRINT_IF_DEBUGGING("test2");
#undef DEBUG

PRINT_IF_DEBUGGING("test3");

would result in:

print("test1");

debugPrint("test2");

print("test3");

2. How IS_DEBUG_DEFINED() works

The fundamental trick behind this is to use concatenation - if the macro was defined it will be expanded, otherwise the token will be left unmodified by the preprocessor:

godbolt

#define CAT(a, b) CAT_IMPL(a, b)
#define CAT_IMPL(a, b) a ## b

// DEBUG NOT DEFINED:
CAT(CHECK_,DEBUG) // will expand to CHECK_DEBUG

// DEBUG DEFINED:
#define DEBUG 1234
CAT(CHECK_,DEBUG) // will expand to CHECK_1234
  • The first CAT will expand to CHECK_DEBUG , because DEBUG was not defined.
  • The second CAT however will expand to CHECK_1234 , because DEBUG was defined and expanded to 1234 before the concatenation with CHECK_ .

By defining a macro named CHECK_DEBUG we can change the result if the macro was not defined, eg:

godbolt

#define TEST CAT(CHECK_,DEBUG), 0, 1
#define CHECK_DEBUG ~,~
  • If DEBUG is not defined the result will be ~, ~, 0, 1 ( 4 comma-separated tokens)
  • If DEBUG is defined the result will be CHECK_, 0, 1 ( 3 comma-separated tokens)

Notice how we got 4 tokens in the first case, but only 3 tokens in the second.

Now all we need to do is take the 3rd token from that sequence (which will be 0 if DEBUG is not defined and 1 otherwise), for example with a simple macro that always returns the 3rd argument:

#define CHECK(a, b, c, ...) c

Putting it all together, this is what a full IS_DEBUG_DEFINED() could look like:

godbolt

#define CAT(a, b) CAT_IMPL(a, b)
#define CAT_IMPL(a, b) a ## b

#define IS_DEBUG_DEFINED() CHECK((CAT(CHECK_,DEBUG), 0, 1))
#define CHECK_DEBUG ~,~
#define CHECK(tup) CHECK_IMPL tup
#define CHECK_IMPL(a, b, c, ...) c

IS_DEBUG_DEFINED() will expand to 0 if DEBUG is not defined, and 1 if it is, eg:

IS_DEBUG_DEFINED() // -> 0

#define DEBUG
IS_DEBUG_DEFINED() // -> 1
#undef DEBUG

IS_DEBUG_DEFINED() // -> 0

With IS_DEBUG_DEFINED() you can then use a standard preprocessor IIF to change the behaviour of your macro depending on wether DEBUG is defined or not.

Example: godbolt

#define IIF(condition, true_value, false_value) CAT(IIF_,condition)(true_value, false_value)
#define IIF_0(true_value, false_value) false_value
#define IIF_1(true_value, false_value) true_value 

#define PRINT_IF_DEBUGGING(format) IIF(IS_DEBUG_DEFINED(), PRINT_DEBUGGING, PRINT_NOT_DEBUGGING)(format)
// this will be used if DEBUG is defined:
#define PRINT_DEBUGGING(format) debugPrint(format)
// this will be used if DEBUG is NOT defined:
#define PRINT_NOT_DEBUGGING(format) print(format)


PRINT_IF_DEBUGGING("test"); // -> print("test");

#define DEBUG
PRINT_IF_DEBUGGING("test"); // -> debugPrint("test");
#undef DEBUG

PRINT_IF_DEBUGGING("test"); // -> print("test");

3. Caveats

One small caveat with this is that if DEBUG is defined it must expand to a valid preprocessing token (so it must only contain letters, digits and underscores) - otherwise the concatenation will result in an error.

So this would not work:

#define DEBUG ()
#define DEBUG +

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM