[Warning: Low-level C stuff ahead!]
Imagine a situation where you want to statically initialize an array with values different to 0:
1 2 3 4 5 6 |
#define ARRAY_SIZE 8 int MY_ARRAY[ARRAY_SIZE] = { 42, 42, 42, 42, 42, 42, 42, 42 }; |
This approach works, at least until someday you want to increase the array size to, say, 200. In this case, you have to add 192 times “42, ” to the initializer list. What a dread!
Everything would be easy, if you wanted to zero-initialize the array:
1 2 3 4 |
#define ARRAY_SIZE 200 int MY_ARRAY[ARRAY_SIZE] = { 0 }; |
With zero-initialization, all you have to do is specify the value of the first element – all of the remaining elements will automatically be set to zero.
But sometimes you need a value different to 0 and you don’t want an additional call to “memset()” at run-time. Or you cannot use “memset()” because your array is stored in a read-only ROM segment and you cannot change the array’s values dynamically.
Basically, what you want is this:
1 2 3 4 5 6 |
#define ARRAY_SIZE 200 int MY_ARRAY[ARRAY_SIZE] = { STATIC_INIT(42, ARRAY_SIZE) }; |
Then, you would only have to make a single change to alter the size of the array (or the initialization value).
Alas, it turns out that it is impossible to define a macro that does the job we expect from “STATIC_INIT”. Think about it for a while. How would you solve this problem?
Sometimes, it is possible to replace a call to an impossible macro with the inclusion of a header file; I call this technique the “Replace macro call with file inclusion” trick:
1 2 3 4 5 6 7 8 |
#define ARRAY_SIZE 200 int MY_ARRAY[ARRAY_SIZE] = { #define STATIC_INIT_VALUE 42 #define STATIC_INIT_COUNT ARRAY_SIZE #include "static_init.h" }; |
The two defines represent the macro parameters and the inclusion of the header file represents the actual macro call.
You probably wonder what the contents of “static_init.h” are, but it’s instructive to spend some time on this problem yourself. Afterwards, you can have a look at my solution.
Note that this approach is not limited to single values – you can also use it for more complex initializations. For instance, if you need an alternating sequence of ’42’ and ’13’ you would do this:
1 2 3 4 5 6 7 8 |
#define ARRAY_SIZE 200 int MY_ARRAY[ARRAY_SIZE] = { #define STATIC_INIT_VALUE 42, 13 #define STATIC_INIT_COUNT (ARRAY_SIZE / 2) #include "static_init.h" }; |
I’ve also used the “Replace macro call with file inclusion” trick to encapsulate #pragmas and other compiler-specific features. Consider the case where you are working on a multi-platform project that uses different compilers. Consider further that you have a piece of code that generates compiler warnings and you want to locally turn compiler warnings off:
1 2 3 4 5 6 7 |
#pragma warning off ... // nasty code ... #pragma warning on |
Now the problem with this approach is that #pragmas are compiler-dependent, which means that you will end up with something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#if COMPILER1 #pragma warning off #elif COMPILER2 #pragma nowarn -save #elif COMPILER3 #nowarn #else #error Compiler not supported. #endif ... // Nasty code ... #if COMPILER1 #pragma warning on #elif COMPILER2 #pragma nowarn -restore #elif COMPILER3 #warn #else #error Compiler not supported. #endif |
Not only does this litter the code – it is also a maintenance nightmare.
Usually, the solution is to encapsulate compiler specific features in #defines; alas this obvious strategy doesn’t work for #pragmas:
1 2 3 4 |
#define WARNINGS_ON #pragma nowarn -save // doesn't work #define WARNINGS_OFF #pragma nowarn -restore // doesn't work |
So it is time to roll out our trick once again:
1 2 3 4 5 6 7 |
#include "warnings_off" ... // Nasty code ... #include "warnings_on" |
Where, for instance, “warnings_off” looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
// warnings_off #if COMPILER1 #pragma warning off #elif COMPILER2 #pragma nowarn -save #elif COMPILER3 #nowarn #else #error Compiler not supported. #endif |
You probably won’t need this trick very often, but when you do, it is good to know that it’s there.