Reputation: 481
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
Reputation: 10975
#ifdef
can't be used inside macros#ifdef
completely within macrosBut 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:
#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
DEBUG
is not defined, then CHECK_IMPL(~,~,0,1)
will end up be called (because CHECK_DEBUG
expands to ~, ~
) (which results in 0
).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:
#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:
#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