Using the C preprocessor to determine current scope?
Asked Answered
C

2

7

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.

Colucci answered 20/8, 2012 at 15:12 Comment(4)
Doesn't clang support nested functions at all? For GCC there's the -fnested-functions switch, isn't that available for clang? And btw why do you need this at compile time? Checking func for NULL or an empty string would do the job, wouldn't it?Font
@H2CO3 Yes, this is a macro to create a static variable loaded from a config, it needs to be compile time. As far as clang not supporting nested functions, read this: clang.llvm.org/docs/UsersManual.html#c_unimpl_gccColucci
thanks, that's interesting (and also sorry...)Font
Btw exactly what are you trying to achieve using this? Shouldn't you redsign your code a bit instead of CPP hackery?Font
N
3
#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.

Nefertiti answered 30/8, 2012 at 18:4 Comment(8)
Yes, you can set an initial value in a constructor block, I used it in my answer on How Tto write an iOS app purley in C?.Colucci
SCOPE_CHOOSER was supposed to expand to one of LOCAL or GLOBAL, dependent upon current scope.Colucci
@RichardJ.RossIII #1 — I mean "I don't think you can solve your problem with __attribute__((constructor))." Obviously you can do what you've already done, but that didn't solve your problem. And #2 — Can you give any examples of preprocessor macros that expand to "one of" two different things depending on context of any sort? My point is that macro expansion is less powerful than expression evaluation; token-pasting a macro that evaluates to the right thing won't help unless you can actually make it expand to the right thing (and nothing but).Nefertiti
Here is an example on IDEone using entirely preprocessor: ideone.com/5vHOt. As you can see, it creates a string based on the number of arguments passed in. Using the same logic, if we could somehow find a way to pass a certain number of arguments based on whether or not we are in function scope, we could use the same thing.Colucci
"I don't think you can load the variable's initial value with __attribute__((constructor))" -- Well, I just used that technique in one of my enterprise apps, so it must work...Font
@RichardJ.RossIII ideone.com/5vHOt It would actually be easy if __FUNCTION__ behaved like a regular macro, but in modern GCC and Clang __FUNCTION__ behaves more like __func__; the preprocessor doesn't expand it. This question/answer has good information: #11143279 ...which I've verified by reading the current Clang codebase. So __FUNCTION__-oriented tricks are right out. Your best bet is still to find a non-preprocessor construct that works in both contexts; or to explain what your ultimate goal is.Nefertiti
I gave you a +1, and I'm going to mess around with the answer you gave. If I can make something out of it, I'll accept and award the bounty as well as add my own answer explaining my entire solution.Colucci
The scope macro almost works in GCC except there is a warning: that you can't silence about __func__.Newbill
N
2

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.

Newbill answered 13/10, 2021 at 9:9 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.