1

How can I ensure COLOR_NAMES is filled to the correct size at compile-time? If a new color is added, say COLOR_4 (and hence N_COLORS is auto incremented), the compiler will then tell me COLOR_NAMES is not filled to size.

Most answers that I found online is for runtime, not compile time.

This is for C-style notation (no STL and other libraries usage).

enum Colors { COLOR_1, COLOR_2, COLOR_3, N_COLORS; }; const char* COLOR_NAMES[N_COLORS] = { /* COLOR_1 */ "Color1", /* COLOR_2 */ "Color2", /* COLOR_3 */ "Color3" }; const char* Blah() { Colors color; ... printf("%s blah blah\n", COLOR_NAMES(color)); } 
4
  • stackoverflow.com/questions/32999822/… Commented Jan 8, 2016 at 3:52
  • 1
    Not quite compile-time, but you can easily check this in a unit test. Commented Jan 8, 2016 at 4:02
  • If it were possible, how would it know what string to use for the new colour? Commented Jan 8, 2016 at 4:15
  • The answer by "Sam" in this previous question stackoverflow.com/questions/712463/… looks pretty clever. Commented Jan 8, 2016 at 5:10

3 Answers 3

2

It is standard practice for such cases of arrays and corresponding enums to compare the "enum size member", N_COLORS in your case, against the number of items in the array.

To get the number of items in an array, simply divide the array size with the size of one array member.

Thus:

_Static_assert(sizeof(COLOR_NAMES)/sizeof(*COLOR_NAMES) == N_COLORS, "Array item missing!"); 

Edit:

Oh btw for this to be meaningful the array declaration must be const char* COLOR_NAMES[] = otherwise you wouldn't be able to tell if there are missing initializers in the array initialization list.

Sign up to request clarification or add additional context in comments.

Comments

1

The ideal would to be able to use sizeof from preprocessor. But we can't because sizeof is evaluated by the compiler.

There are many ways to bypass this but here is quite simple and portable one:

const char* COLOR_NAMES[] = { /* COLOR_1 */ "Color1", /* COLOR_2 */ "Color2", /* COLOR_3 */ "Color3" }; typedef char CHECK_COLOR_NAMES[sizeof(COLOR_NAMES) / sizeof(COLOR_NAMES[0]) == N_COLORS ? 1 : -1]; 

If the test fails, you will attempt to define an array with size -1, which will result into a compilation error.

EDIT: Then we use a typedef to avoid to actually create a variable that we'll not use (Lundin's remark)

8 Comments

When creating a "poor man's static assertion", it is better to use a typedef. That way you don't actually allocate any memory for your nonsense array. Better yet: get a C compiler which is not older than 5 years and you'll have language support for static assert.
And what's the portable way to do static assertion with your one second old compilers ? Thanks.
I think the presence of static assert in C11 alone is a reason to upgrade to that standard. The old "cannot declare an array of size -1 at line 666" compile-time assert errors are not very descriptive :)
Ah okay, thanks ! I've been always reticent about standard static assertions because VS (including VS2015) just doesn't know _Static_assert, and in general simply ignores C11. But I've just learnt that C11 also defines static_assert in assert.h. In VS, this is a keyword, not a macro in assert.h :P. But that matters very little. So I think the best way to combine theory and practice nowadays is to include <assert.h> and use static_assert, for maximum portability. Thank you again Lundin :).
It is because VS is mainly a C++ compiler and static_assert is a keyword in C++. In addition to the _Static assert keyword in C11, it also included static_assert as a macro, for C++ compatibility. Reason why VS2015 in C mode doesn't understand _Static_assert, is because despite the 2015 in the name, it's still an old crap compiler from the mid 90s. I would avoid that compiler completely for pure C programming.
|
0

with constepxr, you can use :

constexpr static const char* COLOR_NAMES[N_COLORS] = { /* COLOR_1 */ "Color1", /* COLOR_2 */ "Color2", /* COLOR_3 */ "Color3" }; static_assert(COLOR_NAMES[N_COLORS-1] !=0); 

if you want to use local variable, you can use variant

constexpr variant<const char*> COLOR_NAMES[N_COLORS] = { /* COLOR_1 */ "Color1", /* COLOR_2 */ "Color2", /* COLOR_3 */ "Color3" }; static_assert(get<0>(COLOR_NAMES[N_COLORS-1]) !=nullptr); 

4 Comments

What if the memory space just outside of COLOR_NAMES is not zero in the first place?
@Ryuu according to this link: stackoverflow.com/a/7760316/440403, if T is not a class(T array[]), then its element will not be initialized. but per stackoverflow.com/a/20916572/440403, static variable will be initialized with default value
@Ryuu for local variable, you can use constexpr together with variant, since variant guarantee the first element will be initialized with default value. I got the idea from here: bfilipek.com/2018/06/variant.html
Cool article. Thanks!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.