krnd
krnd

Reputation: 1

Generic preprocessor macro enabled check

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

Answers (3)

Eric Postpischil
Eric Postpischil

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

grek40
grek40

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

Eric Postpischil
Eric Postpischil

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

Related Questions