Is it possible to detect at compile time if a const array is supplied with too few initializers? (YES!)

I have an array that occasionally grows as I add more states. I want to be sure that I have initialized all the array members. (These examples are simplistic, my array is actually an array of structs that keep track of a system state.)

Example one, no programming errors, compiles fine:

#define NUM_HEX_LETTERS 16
const char *Hex_Letters_As_Strings[NUM_HEX_LETTERS ]=
  {
  "0=0",
  "1=1",
  "2=2",
  "3=3",
  "4=4",
  "5=5",
  "6=6",
  "7=7",
  "8=8",
  "8=9",
  "10=A",
  "11=B",
  "12=C",
  "13=D",
  "14=E",
  "15=F",
 };

Example two, extra initializer, throws: "error: too many initializers for 'const char* [16]" as expected:

#define NUM_HEX_LETTERS 16
const char *Hex_Letters_As_Strings[NUM_HEX_LETTERS ]=
  {
  "0=0",
  "1=1",
  "2=2",
  "3=3",
  "4=4",
  "5=5",
  "6=6",
  "7=7",
  "8=8",
  "8=9",
  "10=A",
  "11=B",
  "12=C",
  "13=D",
  "14=E",
  "15=F",
  "16=G", //Whups -- that is for base 17 !
 };

Example three: Too few initializers. No error. Is there a way to detect this at compile time?

#define NUM_HEX_LETTERS 16
const char *Hex_Letters_As_Strings[NUM_HEX_LETTERS ]=
  {
  "0=0",
  "1=1",
  "2=2",
  "3=3",
  "4=4",
  "5=5",
  "6=6",
  "7=7",
  "8=8",
  "8=9",
  "10=A",
  "11=B",
  "12=C",
  "13=D",
  "14=E",
//Missing initialization for 1 array member. No error.  How can I detect this ?
 };

I tried
#pragma GCC diagnostic error "-Wmissing-field-initializers"
and
#pragma GCC diagnostic error "-Wuninitialized"
but neither seems to detect this kind of programming error.

Is there a way to pick up this kind of dumb programming error?

No, but you can reverse your logic and make the array as large as it will ever need to be and completely fill it. Then make a limiting value for the indexing that changes with your requirements.

1 Like

Thanks for your reply.

The real array contains system state structs: Active touch areas, background image, actions available, etc.

I inserted a new state in one chunk of code (which automatically made the array larger) but forgot to add the corresponding additional struct into the array.

Just a dumb mistake. I was hoping that the newer compilers would have a trap for it.

If I thought it through better, I could merge the two lists, that would probably be better code anyway.

Use static_assert

#define NUM_HEX_LETTERS 16
const char *Hex_Letters_As_Strings[] =  // Let the compiler count for you
{
  "0=0",
  "1=1",
  "2=2",
  "3=3",
  "4=4",
  "5=5",
  "6=6",
  "7=7",
  "8=8",
  "8=9",
  "10=A",
  "11=B",
  "12=C",
  "13=D",
  "14=E",
  //Missing initialization for 1 array member. No error.  How can I detect this ?
  "15=F",  // comment-out to see static_assert failure
};
// sizeof/sizeof[0] required when there is no standard library, like on AVR
static_assert(sizeof(Hex_Letters_As_Strings) / sizeof(Hex_Letters_As_Strings[0]) == NUM_HEX_LETTERS);
// Otherwise, could use 
static_assert(std::extent<decltype(Hex_Letters_As_Strings)>::value == NUM_HEX_LETTERS);
// which can actually be longer, but is not as repetitive

void setup() {}

void loop() {}

It might require an initial build, but when tidying this up, the simple act of commenting or uncommenting the "15=F" put or removed a red squiggle under the static_assert, indicating that the code was either bad or good without having to do a build. I might have some advanced (but not secret) "show squiggles" feature turned on in the IDE though.

On ESP32 at least, an additional message after "static assertion failed" is even more specific: note: the comparison reduces to '(15 == 16)'

1 Like

CLEVER! Awesome! This is exactly what I want! Here is my implementation.

//Check that the unspecified length array "screens" has SS_NUM_STATES  initializers
static_assert(sizeof(SCREEN_STATE)*SS_NUM_STATES == sizeof(screens),"screens array is out of date");

And when I comment one initializer out:

D:\Mini_Brake_Dyno\RP2040_Brake_Dyno\Menu.cpp:742:50: error: static assertion failed: screens array is out of date
  742 | 
      |                                                  ^                 
D:\Mini_Brake_Dyno\RP2040_Brake_Dyno\Menu.cpp:742:50: note: the comparison reduces to '(408 == 396)'

I tried something similar, but since I was specifying the array size it was always true :frowning:

THANKS !!

Here is a preview of the UI I am working on, just for fun:

Peace. Thanks for your help/