Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Checking if an array is partially initialized at compile-time
I have the following code in my project
typedef enum
{
RELAY_CHANNEL_PIN_5,
RELAY_CHANNEL_PIN_6,
RELAY_CHANNEL_PIN_7,
RELAY_CHANNEL_PIN_8,
RELAY_CHANNELS_N
} relay_channel_pin_t;
static const uint8_t relay_arr[] =
{
[RELAY_CHANNEL_PIN_5] = PJ4,
[RELAY_CHANNEL_PIN_6] = PJ5,
[RELAY_CHANNEL_PIN_7] = PJ6,
[RELAY_CHANNEL_PIN_8] = PJ7,
};
_Static_assert(sizeof relay_arr/ sizeof *relay_arr == RELAY_CHANNELS_N,
"Relay channel array has incorrect length");
If I remove (or forget to add) the line [RELAY_CHANNEL_PIN_8] = PJ7, the _Static_assert triggers and stops my build from completing. However, removing the line in the middle of the initialization like [RELAY_CHANNEL_PIN_7] = PJ6, it does not trigger the assert, and my program build successfully, but now the array is partially initialized which I do not desire.
Is there a way in C to check at compile-time if an array is partially initialized?
1 answer
Disclaimer:
There's just so many clever tricks the programmer can use to save the code from themselves: from off-by-1 bugs or code repetition, or whatever one tries to avoid. In the end - if the programmer doesn't know or isn't paying attention to what they are doing, no clever tricks or tools will save the code.
Sure there are almost always ways to achieve what you want, but the question is if you should go there or if you should just attempt to find these kind of slips through debugging and testing. (Static analyzers for example might just find this kind of bug.) The cure isn't necessary an improvement of the code and might add extra complexity, which in turn increases probability of bugs.
That being said, you can solve almost any problem in C by tossing enough ugly macros at it.
Enter X macros:
#define RELAY_LIST(X) \
/* n pin */ \
X(5, PJ4) \
X(6, PJ5) \
X(7, PJ6) \
X(8, PJ7) \
typedef enum
{
#define RELAY_ENUM(n, pin) RELAY_CHANNEL_PIN_##n,
RELAY_LIST(RELAY_ENUM)
RELAY_CHANNELS_N
} relay_channel_pin_t;
static const uint8_t relay_arr[] =
{
#define RELAY_ARR_INIT(n, pin) [RELAY_CHANNEL_PIN_##n] = pin,
RELAY_LIST(RELAY_ARR_INIT)
};
_Static_assert(sizeof relay_arr/ sizeof *relay_arr == RELAY_CHANNELS_N,
"Relay channel array has incorrect length");
Pre-processor output:
typedef enum
{
RELAY_CHANNEL_PIN_5,
RELAY_CHANNEL_PIN_6,
RELAY_CHANNEL_PIN_7,
RELAY_CHANNEL_PIN_8,
RELAY_CHANNELS_N
} relay_channel_pin_t;
static const uint8_t relay_arr[] =
{
[RELAY_CHANNEL_PIN_5] = PJ4,
[RELAY_CHANNEL_PIN_6] = PJ5,
[RELAY_CHANNEL_PIN_7] = PJ6,
[RELAY_CHANNEL_PIN_8] = PJ7,
};
Now you can only slip by messing up the X-macro list. If you slip and miss a line when making that list, then the code that the line would have generated will simply not exist. The enum, the array, everything - adapts to the X-macro list.
The real question is: was it actually worth obfuscating the program this much just for maintainability?
If you have some huge relay plate with 100 relays from 100 GPIO pins, to be ported to several different MCUs with different pinouts, this X-macro design is probably a great idea.
But if you just have some 10-20 relays and won't likely port the code or even switch MCU, probably not - just grab a coffee first and then pay attention when hardcoding the whole pin list. Don't overengineer it.
Alternatively you can generate the whole C source from an external script which your programming IDE runs before compiling. It doesn't even need to be in C and will run on the PC. Then the C source will be clean and readable with everything expanded instead of hidden behind macros. But you have to extra program to maintain and you will need to document whatever IDE sorcery you used to run it, so that you or someone else can do it again, at some point in the future.

0 comment threads