I came across this syntax today and couldn't work out what it meant:
// Uses the GNU C statement expression extension
#define envSet(name) ({ \
static int initialised; \
static bool set; \
(void) "" name; \
if (!initialised || !g_cacheEnv) { \
const char *value = getenv(name); \
set = value != NULL; \
initialised = true; \
} \
set; \
})
The specific line I cannot understand is:
(void) "" name; \
Could somebody please shed some light on this?
It looks like a way to statically ensure that name
is a string literal and not some other type.
If you do (void)"" "hello";
then it is a valid C expression.
But if you do something like (void)"" 1;
then you get a syntax error.
Two consecutive string literals are concatenated. Presumably it's checking whether name
is a string literal. If it isn't, the compiler will report an error.
(void)
cast will supress warnings like "statement with no effect".
Looking at the code, I believe the purpose is to have it call getenv
the first time it is called, cache the result, and then after that use the cached result without having to call getenv
anymore. If getenv
is used with a string literal, then all subsequent calls will ask for the same environment variable; if nothing could change that environment variable they would consequently return the same result. If the code were given a pointer to a string that subsequently changed, the cached result would likely not be correct for the new string, so the purpose of the "" trick is to ensure that can't happen.
Because every string literal that might be used would need to have its own static variable associated with it, the indicated code snippet cannot sensibly be made into a function. On the other hand, the amount of code needed for each repetition does seem a bit much. Further, if the same variable is tested at more than one place in the code, each could end up with its own set of variables and environment-checking code.
Depending upon how the function will be used, it may end up being much faster than code which needs to test an environmental variable every time it's called, and it may be usable from within a function that is called within a loop without advance setup (if client code called an "advance setup" function, the name lookup should be done there, eliminating the need to check within the loop to see if the lookup had been done).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With