C Macro stuff
Let's play with some strangefun C stuff related to macro implementation in GCC compiler.
In the next code, we will work on this kind of structure which is a basic implementation of a matrix structure :
typedef struct {
int width;
int height;
double* data;
} matrix;
In a standard algorithm implementation, we would use malloc() for at least .data field. But, in embedded world, it's preferable to use static allocations (if we use "static" matrices with known parameters).
Use an array as a C macro parameter
First trap of our compiler. We want to statically initalize an array like this :
#define STATIC_ARRAY(_name, _values) double _name[] = _values
STATIC_ARRAY(myArray, {0.0, 0.1, 0.2};
Compilation returns this error :
b.c:5:38: error: macro "STATIC_ARRAY" passed 4 arguments, but takes just 2
STATIC_ARRAY(myArray, {0.0, 0.1, 0.2});
The second argument is not considered as an array, but a list of arguments. My question is WHY ??? This seems a non sense as the goal of macros is to copy paste without interpretation.
Solution I found is to use a variadic macro parameter. This will only works for one array value.
#define STATIC_ARRAY(_name, ...) double _name[] = __VA_ARGS__
STATIC_ARRAY(myArray, {0.0, 0.1, 0.2});
In flight shadow array
Another problem come if I make a more complex macro to initialize my structure. Basically, we may do something like this :
#define STATIC_MATRIX(_name, _width, _height, ...) \
static matrix _name = {.width = _width, .height=_height, .data=__VA_ARGS__}
STATIC_MATRIX(myMat, 2,2, {0.0, 0.1, 0.2, 0.3});
Which give a wonderful error :
b.c:13:1: warning: braces around scalar initializer
STATIC_MATRIX(myMat, 2,2, {0.0, 0.1, 0.2, 0.3});
^
b.c:13:1: note: (near initialization for ‘myMat.data’)
b.c:13:28: error: incompatible types when initializing type ‘double *’ using type ‘double’
STATIC_MATRIX(myMat, 2,2, {0.0, 0.1, 0.2, 0.3});
The correct way to initialize our structure is to declare an array and affect it to .data field. We can embbed this declaration in macro definition :
#define CONCAT_DIRECT(s1, s2) s1##s2
#define CONCAT(s1, s2) CONCAT_DIRECT(s1, s2)
#define STATIC_MATRIX(_mat, _w, _h, ...) \
static double CONCAT(sarr_,__LINE__)[]=__VA_ARGS__;static matrix _mat={.width=_w,.height=_h,.data=CONCAT(sarr_,__LINE__)}
STATIC_MATRIX(myMat, 2,2, {0.0, 0.1, 0.2, 0.3});
This a bit long, but it works ! The macro creates two statics objects, one named by user and one which is unique thanks to line number concatenation. Note, that we cannot split it on two lines due to line number in the name.
We can modify this macro (or create a new one) by removing static attributes. In this case, be careful if you define it in an header, and use it in two separate file at the same line. It could be interesting to use static objects or not depending on variables usage.
Another improvement could be to create const array if data is not updated. Thus, compiler can use a the same memory space for multiple matrix arrays with the same values.
Last but not least. Here, the created array has a variable length that depends on value in parameter. We can fix its size with :
static double CONCATENATE(static_darray_,__LINE__)[_width*_height]