Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Workaround for "semicolon in global scope" warning for no-op C macro

In a codebase that can be built as either C or C++, I thought I'd make a macro to take advantage of static_assert in the case it's built as C++11 or higher.

(Note: I know there are ways to do this in pre-C11 C, at least if you're willing to take a message parameter--though it won't work quite everywhere. But for the sake of argument let's say I have some legitimate need to make it take no message, and be a no-op in at least some C builds.)

So here was the simple definition I tried:

#if defined(__cplusplus) && __cplusplus >= 201103L
    #define STATIC_ASSERT(cond) \
        static_assert((cond), #cond)
#else
    #define STATIC_ASSERT(cond)
#endif

There's no semicolon in the macro, with the intent that you would add it at the callsite. But under pedantic C warning settings, this macro appearing in global scope causes:

error: ISO C does not allow extra ‘;’ outside of a function [-Werror=pedantic]

The easy solution seems to be to take the semicolon off the callsites, and put it in the C++11 side of the macro. But I wonder: how do you make a no-op macro in global scope which allows a semicolon at the callsite (without running afoul of some other warning)?

like image 999
HostileFork says dont trust SE Avatar asked Dec 25 '18 15:12

HostileFork says dont trust SE


Video Answer


1 Answers

Since forward declarations of structures may be repeated as much as we want, you can use a dummy declaration:

#define STATIC_ASSERT(cond) struct GlobalScopeNoopTrick

@JonathanLeffler says this should work in older compilers, even pre-C11...but:

"If you have a C90 compiler, it would object if you had a static assert after a statement in a compound statement. This is not your primary concern (it’ll always be OK at file scope if the static assert is OK too), but it isn’t limited to being used at file scope. The risk is low, though"

For related situations that might not be entirely no-ops at compile-time, C11 introduced the ability to repeat typedefs. And as in the post linked about static asserts in C prior to _Static_assert() shows, there are ways to get around typedef duplication for older C using the line number or another disambiguator:

/* Standard indirection to allow concatenation after expansion */
#define CONCAT(a,b) CONCAT_(a,b)
#define CONCAT_(a,b) a##b

#if defined(__cplusplus) && __cplusplus >= 201103L
    #define STATIC_ASSERT(cond) \
        static_assert((cond), #cond)
#else
    #define STATIC_ASSERT(cond) typedef void CONCAT(dummy_unused_,__LINE__)
#endif

So long as static assertions appear one per line the identifiers won't conflict with each other.

like image 186
3 revs, 2 users 68% Avatar answered Oct 12 '22 11:10

3 revs, 2 users 68%