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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
  • 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? –  Aug 26 '12 at 09:11
  • @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: http://clang.llvm.org/docs/UsersManual.html#c_unimpl_gcc – Richard J. Ross III Aug 26 '12 at 14:48
  • thanks, that's interesting (and also sorry...) –  Aug 26 '12 at 16:07
  • Btw exactly what are you trying to achieve using this? Shouldn't you redsign your code a bit instead of CPP hackery? –  Aug 30 '12 at 19:45

2 Answers2

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.

Quuxplusone
  • 23,928
  • 8
  • 94
  • 159
  • 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?](http://stackoverflow.com/questions/10289890/how-to-write-ios-app-purely-in-c/10290255#10290255). – Richard J. Ross III Aug 30 '12 at 18:39
  • `SCOPE_CHOOSER` was supposed to expand to one of `LOCAL` or `GLOBAL`, dependent upon current scope. – Richard J. Ross III Aug 30 '12 at 18:40
  • @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). – Quuxplusone Aug 30 '12 at 18:47
  • 1
    Here is an example on IDEone using entirely preprocessor: http://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. – Richard J. Ross III Aug 30 '12 at 19:25
  • "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... –  Aug 30 '12 at 19:44
  • @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: http://stackoverflow.com/questions/11142779/function-and-friends-act-weird-in-xcode ...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. – Quuxplusone Aug 30 '12 at 20:24
  • 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. – Richard J. Ross III Aug 30 '12 at 21:28
  • The scope macro almost works in GCC except there is a `warning:` that you can't silence about `__func__`. – szmoore Oct 13 '21 at 08:34
1

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.

szmoore
  • 924
  • 10
  • 18