Reputation: 1
Is there a way to define a macro checking if another macro is defined and non-zero or respectively not defined or defined as zero?
I recently ran into the problem that different people define their macros in a different way if they are used as flags. While compiling some define their macro without a value (eg. -DDEBUG
) and some set it to non-zero (eg. -DDEBUG=1
).
I actually found code similar to it: (defined(macro ## _SET) && (macro ## _SET))
. The solution for this to work is the concatenation ##
which prevents the macro expansion. Is there a way around it?
Environment
GNU Arm Embedded Toolchain (arm-none-eabi-*
) with GNU ISO C99 (std=gnu99
).
Upvotes: 0
Views: 443
Reputation: 222272
This provides a preprocessor test for the cases where a macro is undefined, defined but blank, defined to be “0”, or defined to be “1”. It does not distinguish other definitions for the macros (e.g., a macro defined to be “2” will be reported as not enabled):
#define EnabledTest 1
#define EnabledTest0 0
#define EnabledTest1 1
#define EnabledPaste(x) EnabledTest ## x
#define Enabled(x) EnabledPaste(x)
#define DefinedBlank
#define DefinedZero 0
#define DefinedOne 1
#include <stdio.h>
int main(void)
{
#if Enabled(Undefined)
printf("Undefined is enabled.\n");
#endif
#if Enabled(DefinedBlank)
printf("DefinedBlank is enabled.\n");
#endif
#if Enabled(DefinedZero)
printf("DefinedZero is enabled.\n");
#endif
#if Enabled(DefinedOne)
printf("DefinedOne is enabled.\n");
#endif
}
the output is:
DefinedBlank is enabled.
DefinedOne is enabled.
Additionally, we can combine it with grek40’s idea to generalize it to undefined, defined but blank, and defined to strings that evaluate simply in place of x
in 1 - x - 1
:
#define EnabledTest 1
#define EnabledPaste(x) EnabledTest ## x
#define Enabled(x) (EnabledPaste(x)) || (1 - x - 1)
#define DefinedBlank
#define DefinedZero 0
#define DefinedOne 1
#include <stdio.h>
int main(void)
{
#if Enabled(Undefined)
printf("Undefined is enabled.\n");
#endif
#if Enabled(DefinedBlank)
printf("DefinedBlank is enabled.\n");
#endif
#if Enabled(DefinedZero)
printf("DefinedZero is enabled.\n");
#endif
#if Enabled(DefinedOne)
printf("DefinedOne is enabled.\n");
#endif
}
Upvotes: 0
Reputation: 13438
Quoting some GCC docs: If the defined operator appears as a result of a macro expansion, the C standard says the behavior is undefined. GNU cpp treats it as a genuine defined operator and evaluates it normally.
Meaning, you can't generally hide the defined
macro test within some nested macro expansion, unless you rely on compiler specific non-standard extensions.
So, the best I could come up with involves a two-part test (https://stackoverflow.com/a/13262601/5265292 gave me the basic idea):
// if x is empty: 1 - -1 == 2
// if x is zero: 1 - 0 - 1 == 0
// if x is any other number: 1 - x - 1 == -x
#define ENABLED(x) (1 - x -1 != 0)
#if defined FLAG && ENABLED(FLAG)
// execute if FLAG is defined as not-zero
#endif
Complete sample:
#include <stdio.h>
#define TEST1
#define TEST2 0
#define TEST3 1
// define TEST4 // not defined
#define ENABLED(x) (1 - x -1 != 0)
int main(void)
{
#if defined TEST1 && ENABLED(TEST1)
printf("TEST1 is enabled!\n");
#endif
#if defined TEST2 && ENABLED(TEST2)
printf("TEST2 is disabled!\n");
#endif
#if defined TEST3 && ENABLED(TEST3)
printf("TEST3 is enabled!\n");
#endif
#if defined TEST4 && ENABLED(TEST4)
printf("TEST4 is disabled!\n");
#endif
return 0;
}
As pointed out, you might be able to utilize the GNUCC specific macro extensions to have the defined
test within the ENABLED
test, but I don't have GCC and the time at hand to check this aspect and I prefer portable solutions.
Upvotes: 2
Reputation: 222272
You can stringify the macro name (if undefined) or the expansion (if defined) and then examine it. This program:
#define DefinedBlank
#define DefinedZero 0
#define DefinedOne 1
#define Stringify(x) #x
#define Enabled(x) (Stringify(x)[0] == 0 || Stringify(x)[0] == '1')
#include <stdio.h>
int main(void)
{
const char *String[] = { "not ", "" };
printf("Undefined is %senabled.\n", String[Enabled(Undefined)]);
printf("DefinedBlank is %senabled.\n", String[Enabled(DefinedBlank)]);
printf("DefinedZero is %senabled.\n", String[Enabled(DefinedZero)]);
printf("DefinedOne is %senabled.\n", String[Enabled(DefinedOne)]);
}
prints:
Undefined is not enabled.
DefinedBlank is enabled.
DefinedZero is not enabled.
DefinedOne is enabled.
Apple LLVM evaluates the conditions at compile time, so you could use if Enabled(foo) { code… }
, and the code…
would not be included in the executable if foo
were not enabled. (However, it must be syntactically and grammatically correct.)
If you want more sophisticated distinctions than just undefined, blank, 0, or 1, then you can write an arbitrary routine to examine the string and return a result. Of course, the compiler is less likely to optimize that.
Upvotes: 2