11

I would like to write code in C something like this:

if(defined(MACRO))
  ...
else
  ...

but I could not find any way to do this in C, since the defined(MACRO) preprocessor operator works only inside #if's. Is there a way to do this?

What I really like to do is to write:

ASSERT(UART, var >= 0);

where

#define ASSERT(NAME, TEST) \
  do { \
    if (defined(NAME) && !(TEST)) \
      printf("Assert failed"); \
  } while(0)

thus I could turn on ASSERT checks when a macro is defined and if it is not defined, then the asserts should not be checked. If you try to do this, then you get:

implicit declaration of function `defined'

which is quite understandable since the GCC compiler does not find the defined() preprocessor operator.

1

3 Answers 3

13

A macro by comex is expanded to 1 if the argument is defined to 1. Otherwise it is expanded 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 trick is based on variadic function-like macros (...) and preprocessor token concatenation (##).

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

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

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

Sign up to request clarification or add additional context in comments.

5 Comments

The referenced link does not work (anymore). That is the general problem with qestions and answers relying on external links without at least citing the relevant part. Note this is against site-rules and a valid downvote reason.
@Olaf, the link works for me. It looks like a valid link to user profile on G+: https://plus.google.com/u/{id}/{id}/posts
Yes, the link points to G+ profile of the person who came up with the idea.
So it is some incompatibility with browsers or geo-locking. Whatever it is, this is against (current) site-rules. Answers have to be self-contained.
The answer is self-contained.
3

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

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

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

if( DEFINED(MACRO) )
  ...

3 Comments

Keep in mind though that this uses C statements and will only patrly be expanded in the precompiler. As I understand, the C-Compiler then realises that the result of the condition is static and eliminates unused code...
Cool, but it only works for empty macros... That is if you do #define M y, then DEFINED(M) will return 0 (tested with gcc 4.5.1)
What previous post do you mean? It doesn't fit on my view. What sort order do you use?
2

Why don't you simply define ASSERT differently 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 values in C conditionals should be avoided - sure the compiler should optimise the dead code out, but why rely on that when you can essentially remove the actual C code?

EDIT:

There is a rather ugly trick involving macro argument stringification that you 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 it uses the fact that when a preprocessor token is not defined as a macro, it expands to itself. When, on the other hand, it is defined it expands to either an empty string, or its definition. Unless one of your macros has its own name as a definition, this hack should work.

Disclaimer: Use this at your own risk!

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

EDIT 2:

The assembly output of gcc -O0 -S for the program above 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 this program to a single puts() call. This program produces exactly the same assembly output:

#include <stdio.h>

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

    return 0;
}

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

6 Comments

No, that is no good. I always want to define ASSERT, but if NAME is undefined, then I do not want to do anything. Also, I do not want to enclose every use of ASSERT with an #ifdef
@Miklos Maroti: So the definition of NAME is not global, while ASSERT is, and you want ASSERT to work only when NAME is defined?
I want to write code like this: ASSERT(UART, var >= 0); ASSERT(SPI, var == 0); So if I defined UART, then all uart assertions should be turned on, if I define SPI, then all spi assertions should be turned on, and I do not want a predefined fixed number of subsystems, i.e. ASSERT_UART, ASSERT_SPI is not a good solution.
@Miklos Maroti: I suppose that always defining those macros to either 0 or 1 is not something that you want either?
Yes, I do not want to define any of these, so then all of them are disabled.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.