[ACCEPTED]-Using defined(MACRO) inside the C if statement-c-preprocessor

Accepted answer
Score: 13

A macro by comex is expanded to 1 if the argument 21 is defined to 1. Otherwise it is expanded 20 to 0:

#define is_set(macro) is_set_(macro)
#define macrotest_1 ,
#define is_set_(value) is_set__(macrotest_##value)
#define is_set__(comma) is_set___(comma 1, 0)
#define is_set___(_, v, ...) v

You can use it as follows:

if (is_set(MACRO)) {
   /* Do something when MACRO is set */
}

Explanation: The 19 trick is based on variadic function-like 18 macros (...) and preprocessor token concatenation 17 (##).

  1. is_set is simply a wrapper to facilitate the expansion of its parameter.
  2. is_set_ tries to concatenate macrotest_ with the evaluated 16 value of its input (comma). If its input is defined, then 15 this works; otherwise is_set__ is called with macrotest_<macro> where 14 <macro> is the original argument to is_set (e.g., is_set(foo) leads 13 to macrotest_foo if foo is not a defined macro).

  3. In is_set__ its 12 parameter is again expanded but this only 11 works out if it is passed macrotest_1. If it is, then 10 is_set___(, 1, 0) is called because comma evaluates to , (note 9 the 3 parameters!). For any other value 8 of comma (i.e., if the macro to be tested 7 is undefined or has any other (expanded) value 6 than 1 the parameter can not be expanded 5 and thus is_set___(macrotest_<macro> 1, 0) is called, which has only 2 arguments.

  4. Eventually, is_set___ simply 4 selects its second parameter for its "output" and 3 drops everything else. Due to the behavior 2 of is_set__ this leads to either 1 if the macro to 1 be tested is defined and 1, or 0 otherwise.

Score: 4

Ok, based on the previous post I got this 6 idea, which seems to work:

#define DEFINEDX(NAME) ((#NAME)[0] == 0)
#define DEFINED(NAME) DEFINEDX(NAME)

This will check 5 if NAME is defined and therefore it expands 4 to the empty string with 0 at its first 3 character, or it is undefined in which case 2 it is not the empty string. This works with 1 GCC, so one can write

if( DEFINED(MACRO) )
  ...
Score: 2

Why don't you simply define ASSERT differently 22 depending on that macro?

#ifdef MACRO
#define ASSERT(NAME, TEST) \
    do { \
        printf("Assert failed"); \
    } while(0)
#else
#define ASSERT(NAME, TEST) {}
#endif

Using fixed preprocessor 21 values in C conditionals should be avoided 20 - sure the compiler should optimise the 19 dead code out, but why rely on that when 18 you can essentially remove the actual C 17 code?

EDIT:

There is a rather ugly trick involving 16 macro argument stringification that you 15 might be able to use:

#include <string.h>
#include <stdio.h>

#define X

#define ERROR_(NAME, TEXT) \
        if (strcmp("", #NAME) == 0) \
                printf("%s\n", TEXT)
#define ERROR(n, t) ERROR_(n, t)

int main() {
    ERROR(X, "Error: X");
    ERROR(Y, "Error: Y");

    return 0;
}

This outputs:

$ ./test
Error: X

Essentially 14 it uses the fact that when a preprocessor 13 token is not defined as a macro, it expands 12 to itself. When, on the other hand, it is defined 11 it expands to either an empty string, or 10 its definition. Unless one of your macros 9 has its own name as a definition, this hack should 8 work.

Disclaimer: Use this at your own risk!

(...because 7 I will most certainly not use it!)

EDIT 2:

The 6 assembly output of gcc -O0 -S for the program above 5 is:

        .file   "test.c"
        .section        .rodata
.LC0:
        .string "Error: X"
        .text
.globl main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        movl    $.LC0, %edi
        call    puts
        movl    $0, %eax
        leave
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.3"
        .section        .note.GNU-stack,"",@progbits

Even with no optimisation, GCC reduces 4 this program to a single puts() call. This program 3 produces exactly the same assembly output:

#include <stdio.h>

int main() {
    puts("Error: X");

    return 0;
}

Therefore, you 2 are probably not going to have any performance issues, depending 1 on your compiler and any optimisations...

More Related questions