0

Believe it or not, I want to use static_assert in a macro that expands to a designated initializer:

#define INIT(N) \
        /* static_assert((N) < 42, "too large"), */ \
        [(N)] = (N)

int array[99] = { INIT(1), INIT(2), INIT(42) };

I want an error from INIT(42), but uncommenting the static_assert is a syntax error. AFAIK static_assert is syntactically a declaration. How can I use it in this example?

not-a-user
  • 4,088
  • 3
  • 21
  • 37

1 Answers1

7
#define INIT(N) \
    [(N)] = (sizeof((struct {_Static_assert((N) < 42, "too large");char c[N];}){{0}}.c))

... I'm not sure myself how I ended up with that abomination. But hey, it works (for N > 0) !

// A struct declaration is a valid place to put a static_assert
        struct {_Static_assert((N) < 42, "too large");          }
// Then we can place that declaration in a compound literal...
       (struct {_Static_assert((N) < 42, "too large");          }){   }
// But we can't just throw it away with `,`: that would yield a non-constant expression.
// So let's add an array of size N to the struct...
       (struct {_Static_assert((N) < 42, "too large");char c[N];}){{0}}
// And pry N out again through sizeof!
sizeof((struct {_Static_assert((N) < 42, "too large");char c[N];}){{0}}.c)

0-friendly version (just adding then subtracting 1 so the array has a positive size):

#define INIT(N) \
    [(N)] = (sizeof((struct { \
        _Static_assert((N) < 42, "too large"); \
        char c[(N) + 1]; \
    }){{0}}.c) - 1)
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • Great! But no need to create a compound literal, just take `sizeof` the anonymous `struct`. – not-a-user Apr 27 '17 at 19:49
  • @not-a-user no, that wouldn't be portable -- in fact, I reckon common aligmnent rules would make, for example, `sizeof` equal 4 with N = 3. But nothing is actually created anyway, because we're in an unevaluated context :) – Quentin Apr 27 '17 at 19:52
  • What about `#define ZASSERT(x, m) (0 * sizeof(struct{_Static_assert((x), m); char dummy;}))` and `#define INIT(N) [(N)] = (N) + ZASSERT((N) < 42, "too large")`? Looks portable and reusable to me. – not-a-user Apr 28 '17 at 06:39
  • @not-a-user yes, that looks fine. – Quentin Apr 28 '17 at 07:49