12

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)?

  • 3
    `#define STATIC_ASSERT(cond) void never_called()` -- the semicolon completes the forward declaration. – Raymond Chen Dec 25 '18 at 16:29
  • @RaymondChen It seems in some compiler/warning combinations you get warnings about having forward function declarations more than once. You can disable those, but assuming they are there for a good reason...the struct solution below doesn't appear to trigger any warnings. – HostileFork says dont trust SE Dec 25 '18 at 16:42

1 Answers1

15

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.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 3
    There is no need for `__LINE__` or concatenation. Type definitions may be repeated, so any name that will not be otherwise used is fine; one can use `typedef void VoidTypeKludge`. – Eric Postpischil Dec 25 '18 at 16:00
  • 4
    You could also just use `struct StaticAssertSurrogate` as the replacement text, which will work with compilers older than C11 (which is when it became possible to repeat a typedef). It doesn't even matter whether the structure tag exists as a real structure type. – Jonathan Leffler Dec 25 '18 at 16:04
  • @JonathanLeffler - Thanks, I was just in the process of checking typedef redefinition rules in C89. – StoryTeller - Unslander Monica Dec 25 '18 at 16:06
  • 1
    C11 [§6.7 Declarations ¶3](http://port70.net/~nsz/c/c11/n1570.html#6.7p3). The wording about _a typedef name may be redefined to denote the same type as it currently does_ is new. – Jonathan Leffler Dec 25 '18 at 16:13
  • @HostileFork - I stepped out shortly after editing the alternative in. I marked it as CW though (only thing that's relatively easy on mobile). If you don't mind, make the edit. Or I'll get back to it later. – StoryTeller - Unslander Monica Dec 25 '18 at 16:25
  • @HostileFork — 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. – Jonathan Leffler Dec 25 '18 at 16:28