Using the C preprocessor to determine current scope?

Viewed 1593

I am developing an application in C / Objective-C (No C++ please, I already have a solution there), and I came across an interesting use case.

Because clang does not support nested functions, my original approach will not work:

#define CREATE_STATIC_VAR(Type, Name, Dflt) static Type Name; __attribute__((constructor)) void static_ ## Type ## _ ## Name ## _init_var(void) { /* loading code here */ }

This code would compile fine with GCC, but because clang doesn't support nested functions, I get a compile error:

Expected ';' at end of declaration.

So, I found a solution that works for Clang on variables inside a function:

#define CREATE_STATIC_VAR_LOCAL(Type, Name, Dflt) static Type Name; ^{ /* loading code here */ }(); // anonymous block usage

However, I was wondering if there was a way to leverage macro concatenation to choose the appropriate one for the situation, something like:

#define CREATE_STATIC_VAR_GLOBAL(Type, Name, Dflt) static Type Name; __attribute__((constructor)) void static_ ## Type ## _ ## Name ## _init_var(void) { /* loading code here */ }
#define CREATE_STATIC_VAR_LOCAL(Type, Name, Dflt) static Type Name; ^{ /* loading code here */ }(); // anonymous block usage

#define SCOPE_CHOOSER LOCAL || GLOBAL
#define CREATE_STATIC_VAR(Type, Name, DFLT) CREATE_STATIC_VAR_ ## SCOPE_CHOOSER(Type, Name, Dflt)

Obviously, the ending implementation doesn't have to be exactly that, but something similar will suffice.

I have attempted to use __builtin_constant_p with __func__, but because __func__ is not a compile-time constant, that wasn't working.

I have also tried to use __builtin_choose_expr, but that doesn't appear to work at the global scope.

Is there something else I am missing in the docs? Seems like this should be something fairly easy to do, and yet, I cannot seem to figure it out.

Note: I am aware that I could simply type CREATE_STATIC_VAR_GLOBAL or CREATE_STATIC_VAR_LOCAL instead of messing with macro concatenation, but this is me attempting to push the limits of the compiler. I am also aware that I could use C++ and get this over with right away, but that's not my goal here.

2 Answers

Inspired by the @Qxuuplusone answer.

The suggested macro for AT_GLOBAL_SCOPE does indeed work (in GCC), but causes a compiler warning (and I am pretty sure it cannot be silenced by Diagnostic Pragma because it's created by pedwarn with a test here).

Unless you turn on -w you will always see these warnings and have, in the back of your mind, a horrible feeling that you probably shouldn't be doing whatever it is that you are doing.

Fortunately, there is a solution that can silence these lingering doubts. In the Other Builtins section, there is __builtin_FUNCTION with this very interesting description (emphasis mine):

This function is the equivalent of the __FUNCTION__ symbol and returns an address constant pointing to the name of the function from which the built-in was invoked, or the empty string if the invocation is not at function scope.

It turns out, at least in version 8.3 of GCC, you can do this:

#define AT_GLOBAL_SCOPE (__builtin_FUNCTION()[0] == '\0')

This still probably won't answer the original question, but until GCC decides this too will cause a warning (it kind of seems like it's intentionally designed not to though), it lets me continue doing questionable things using macros without anything to warn me that it's a bad idea.

Related