Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using the C preprocessor to determine current scope?

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.

like image 838
Richard J. Ross III Avatar asked Nov 04 '22 17:11

Richard J. Ross III


1 Answers

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

The biggest difficulty here is that the C preprocessor works by textual substitution, so even if you figured out how to get SCOPE_CHOOSER to do what you want, you'd end up with a macro expansion that looked something like

CREATE_STATIC_VAR_LOCAL || GLOBAL(Type, Name, Dflt);

There's no way to get the preprocessor to "constant-fold" macro expansions during substitution; the only time things are "folded" is when they appear in #if expressions. So your only hope (modulo slight handwaving) is to find a single construction that will work both inside and outside of a function.

Can you explain more about the ultimate goal here? I don't think you can load the variable's initial value with __attribute__((constructor)), but maybe there's a way to load the initial value the first time the function body is entered... or register all the addresses of these variables into a global list at compile-time and have a single __attribute__((constructor)) function that traverses that list... or some mishmash of those approaches. I don't have any specific ideas in mind, but maybe if you give more information something will emerge.

EDIT: I don't think this helps you either, since it's not a preprocessor trick, but here is a constant-expression that will evaluate to 0 at function scope and 1 at global scope.

#define AT_GLOBAL_SCOPE __builtin_types_compatible_p(const char (*)[1], __typeof__(&__func__))

However, notice that I said "evaluate" and not "expand". These constructs are compile-time, not preprocessing-time.

like image 200
Quuxplusone Avatar answered Nov 10 '22 13:11

Quuxplusone