DrHell
DrHell

Reputation: 481

How to have a C++ macro for debug, which can change its content

I want to use a macro for debugging purpose. On the same time I want to be able to disable its content for pieces of code that I think have been tested enough. This without complex operations, without changing the code, and so on.

My first idea was to have in a file tested enough a structure like this one

...code that might need debugging...
#define DONT_DEBUG__
... code that I consider safe and tested...
#undef DONT_DEBUG__
...code that might need debugging...

To make this work, I thought to have my debug macro to be like this

#define checkpoint(...) \
    #ifdef DONT_DEBUG__ \
    {}
    #else \
    {... code for degubbing purpose... }\
    #endif

Of course, I sadly discovered that #ifdef can't be used within a #define. And inverting the order of #define and #ifdef won't accomplish what I want to obtain, since it won't allow to switch the macro behavior, once defined.

Of course I might use a global variable, to contain the "DONT_DEBUG__" value, and make the macro act accordingly

static bool DONT_DEBUG__=true;
#define checkpoint(...) \
    {if (DONT_DEBUG__) \
    {}
    else \
    {... code for degubbing purpose... }}\
#endif

But adding a useless "if then else" construct in some cases -ie: very short, inlined functions which just make some arithmetic computations, that I want to check for not nan and not infinite return values in debug phase- would be a price to big to pay.

Is there any solution? A similar construct of #ifdef that can, indeed, be used within a macro?

Upvotes: 2

Views: 422

Answers (1)

Turtlefight
Turtlefight

Reputation: 10975

  • #ifdef can't be used inside macros
  • There's unfortunately no way to emulate #ifdef completely within macros

But in case all you want to check for is if a macro is defined or not there's a neat workaround you can use: checking if the macro would expand or not.

The basic idea behind it is to concatenate the macro with some prefix - if the macro has been defined it will be expanded first - otherwise the macro name will be concated:

godbolt

#define CONCAT(a, b) CONCAT_IMPL(a, b)
#define CONCAT_IMPL(a, b) a ## b

CONCAT(CHECK_,DEBUG) // => CHECK_DEBUG

#define DEBUG

CONCAT(CHECK_,DEBUG) // => CHECK_

Utilizing this we can define a macro CHECK_DEBUG so that when DEBUG is not defined it will be expanded further:
godbolt

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

#define DEBUG
IS_DEBUG_DEFINED() // => 1
#undef DEBUG
IS_DEBUG_DEFINED() // => 0
  • The basic idea here is that if DEBUG is not defined, then CHECK_IMPL(~,~,0,1) will end up be called (because CHECK_DEBUG expands to ~, ~) (which results in 0).
  • But when DEBUG is defined then CHECK_IMPL(CHECK_,0,1) will be called (DEBUG got expanded before it got concatenated) (which results in 1).

So this basically gives you an easy way to check if a macro is defined or not. Then you only need a basic preprocessor if to do something depening on wether or not the macro is defined:

godbolt

#define IIF(value) CONCAT(IIF_,value)
#define IIF_0(true_value, false_value) false_value
#define IIF_1(true_value, false_value) true_value

#define DO_SOMETHING() \
  IIF(IS_DEBUG_DEFINED())( \
    /* DEBUG is defined */ \
    printf("DEBUG IS DEFINED!"); \
  , \
    /* DEBUG is NOT defined */ \
    printf("NO DEBUGGING TODAY!"); \
  )


// Example:
#define DEBUG

DO_SOMETHING() // => printf("DEBUG IS DEFINED!");

#undef DEBUG

DO_SOMETHING() // => printf("NO DEBUGGING TODAY!");

There's one small caveat with that technique though, namely that the macro you want to test needs to expand to a valid preprocessor identifier. This is required because concatenation requires the resulting token to be a valid identifier.

So as long as the macro to be tested expands to only letters, digits and underscores this will work.
Everything else will result in a preprocessor error.

Examples:

// Those are ok:
#define DEBUG
#define DEBUG 123
#define DEBUG foobar
#define DEBUG 1a2b
// ...

// Those won't work: (preprocessor error)
#define DEBUG ()
#define DEBUG +
#define DEBUG a, b, c
// ...

But that shouldn't be a problem for most usecases.


Here's a full example for your DONT_DEBUG__ macro:

godbolt

#define CONCAT(a, b) CONCAT_IMPL(a, b)
#define CONCAT_IMPL(a, b) a ## b

#define IS_DONT_DEBUG_DEFINED() CHECK((CONCAT(CHECK_,DONT_DEBUG__), 0, 1))
#define CHECK_DONT_DEBUG__ ~,~
#define CHECK(tuple) CHECK_IMPL tuple
#define CHECK_IMPL(a, b, c, ...) c

#define IIF(value) CONCAT(IIF_,value)
#define IIF_0(true_value, false_value) false_value
#define IIF_1(true_value, false_value) true_value

#define DO_SOMETHING() \
  IIF(IS_DONT_DEBUG_DEFINED())( \
    /* DONT_DEBUG__ is defined */ \
    printf("NOT DEBUGGING TODAY!"); \
  , \
    /* DONT_DEBUG__ is NOT defined */ \
    printf("DO THE THING!"); \
  )

#define DONT_DEBUG__

DO_SOMETHING() // printf("NOT DEBUGGING TODAY!");

#undef DONT_DEBUG__

DO_SOMETHING() // printf("DO THE THING!");

Upvotes: 0

Related Questions