1

I have code containing many lines such as:

static_assert(sizeof(my_stuct)==42, "check struct size");

and I want to compile on a compiler which has no static_assert implemented. So I want these lines to become no-op. I've tried:

#define static_assert(COND, MSG) 

it works fine but I get a warning with clang compiler:

warning: extra ';' outside of a function [-Wextra-semi]

Is there a simple no-op C statement to be used outside functions which terminates with a semicolon and which can be used repeatedly?

Emanuele Paolini
  • 9,912
  • 3
  • 38
  • 64

4 Answers4

3

I want to compile on a compiler which has no static_assert implemented. So I want these lines to become no-op.

Why not combining Lundin's answer (checking whether the current compiler has it implemented or not) with an implementation of static_assert (which is not hard to do)?

Copying the implementation from PDCLib (which is CC0 licensed):

/* with dodgy implementations, you could also #ifndef static_assert */
#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112
#define _PDCLIB_cc( x, y )     x ## y
#define _PDCLIB_concat( x, y ) _PDCLIB_cc( x, y )

#define static_assert( e, m ) enum { _PDCLIB_concat( _PDCLIB_assert_, __LINE__ ) = 1 / ( !!(e) ) }
#endif

For a given expression e and message m, this declares an anonymous enumeration with one member, the name of which is _PDCLIB_assert_ concatenated with the current source file line (__LINE__) (so you can have multiple static_assert() per source file). This member is set to 1 divided by 1 if the expression e is true, or divided by 0 if the expression is false, which leads to output like this for a failed assert:

./path/to/source.c:57:94: warning: division by zero [-Wdiv-by-zero]
 #define static_assert( e, m ) enum { _PDCLIB_concat( _PDCLIB_assert_, __LINE__ ) = 1 / ( !!(e) ) }
                                                                                      ^

./path/to/header.h:571:1: note: in expansion of macro 'static_assert'
 static_assert( false, "test" );
 ^
./path/to/source.c:57:62: error: enumerator value for '_PDCLIB_assert_571' is not an integer constant
...

It's not pretty, but it's perfectly C89-compliant, tested, and servicable.

Feel free to rename from _PDCLIB_* to anything you prefer.

DevSolar
  • 67,862
  • 21
  • 134
  • 209
  • Thanks! I actually had a C99 implementation of static_assert which, unfortunately, was generating warnings about unused variables. – Emanuele Paolini Aug 30 '18 at 11:50
  • `static_assert` exists as a non-standard extension and also as a C++ keyword. So the absence of C11 is no guarantee of the absence of `static_assert`, should we allow portability to non-standard C or C++. – Lundin Aug 30 '18 at 11:53
  • @EmanuelePaolini: I'd like to give credit to the guys who helped me forging this implementation, but I cannot remember who they were or where we had the discussion about it, sooo... you're welcome, anyway. ;-) – DevSolar Aug 30 '18 at 11:53
  • As for the assertion itself, it looks similar to the icky one somewhere in the Linux kernel. If I have to use older C, I like to use something like `#define STATIC_ASSERT(expr) {typedef uint8_t STAT_ASSERT[(expr) ? 1 : 0];}` Much more readable. – Lundin Aug 30 '18 at 11:57
  • @Lundin: Should we check for `#if ( __cplusplus < 201103L ) || ! defined( static_assert )`? Would that hold for non-standard C and C++? – DevSolar Aug 30 '18 at 11:59
  • @Lundin: There is something broken with your alternative implementation (`expected identifier or '(' before '{' token`). – DevSolar Aug 30 '18 at 12:03
  • It will work in C, but what happens if you put `! defined( static_assert )` in a conforming C++ implementation? Won't it moan about using keywords incorrectly? – Lundin Aug 30 '18 at 12:04
  • I'm not sure if my solution works at file scope (because I never use assert there). Also include stdint.h, though the type `uint8_t` isn't really necessary, could as well use `int`. – Lundin Aug 30 '18 at 12:05
  • @Lundin: Well, the implementation I provided was intended to be a fully standard-compliant implementation of `static_assert()`, which *needs* to work at file scope. As OP is looking at code already *containing* `static_assert`, quite possibly _at_ file scope, it's IMHO better to give him a compliant solution rather than a pretty one. ;-) – DevSolar Aug 30 '18 at 12:10
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/179108/discussion-between-devsolar-and-lundin). – DevSolar Aug 30 '18 at 12:11
2

My solution so far:

#define static_assert(COND, MSG) extern int __static_assert_dummy__

works fine but seems ugly to me!

Emanuele Paolini
  • 9,912
  • 3
  • 38
  • 64
2

Tested only very quickly, but how about:

#define static_assert(COND, MSG) extern int _no_static_assertion

or something? Repeating an extern should be fine.

P.W
  • 26,289
  • 6
  • 39
  • 76
unwind
  • 391,730
  • 64
  • 469
  • 606
2

Since this is a C11 feature, the answer is quite trivial: simply check if the compiler supports C11.

#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112
  static_assert(sizeof(my_stuct)==42, "check struct size");
#endif 

The opposite, to make this a no-op in the absence of standard C, and thereby remove the need for the above compiler switches all over the place, is this:

#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112
  #define static_assert(cond, str) struct dummy_t
#endif 

Though remember that static_assert requires #include <assert.h>, unlike the C11 keyword _Static_assert.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Yes, in fact I actually wrote the code like that ("the opposite"). The point is that I get the warning about extra semicolon, which is annoying – Emanuele Paolini Aug 30 '18 at 11:17
  • Unfortunately I get a warning: warning: redefinition of typedef 'dummy_t' is a C11 feature [-Wtypedef-redefinition] – Emanuele Paolini Aug 30 '18 at 11:22
  • @EmanuelePaolini Ah right you can't have several static_assert in same scope with the `typedef` method. Hmm... how about this? `struct dummy_t`. Forward-declaring a struct. You can do this multiple times in the same scope. – Lundin Aug 30 '18 at 11:30
  • Reason why I want to avoid `extern` is because you might have static analysers etc set to block the use of `extern` anywhere in your code (anti-spaghetti checks). I have that in some projects. – Lundin Aug 30 '18 at 11:32
  • yes, this is better than the "extern" solution. About my removed comment "avoiding to add symbols to namespace" is only a question to avoid possible (even if unlikely) name clashes. – Emanuele Paolini Aug 30 '18 at 11:45
  • @EmanuelePaolini I can't come up with anything at file scope containing a `;` which is not part of a declaration, so I don't think the namespace clutter can be avoided. – Lundin Aug 30 '18 at 11:49