Reputation: 479
I am trying to write a special type handling for array data redundancy. The idea is to define and declare an array globally at compile time with fixed size, but the size is different to each declared array. This is the idea:
array[N] = { el1, el2, el3, ..., elN };
At first, I used the following syntax, which works:
#define ARRAY_DEFDEC(name, size, ...) \
int arr_##name[size] = { __VA_ARGS__ }
When I use this macro I get the expected result:
// C code
ARRAY_DEFDEC(array_test, 7, 1, 2, 3, 4, 5, 6, 7);
// Preprocessed
int arr_array_test[7] = { 1, 2, 3, 4, 5, 6, 7 };
Now, for the problem I am having and don't know if it is possible to solve. While doing this, I also need to create a second array for which all the values will be inverted (using ~ operator or alternatively (0 - element + 1)). I have tried ~VA_ARGS, but naturally, it will only change the first element (in the above example with arr_array_test I will get -2, 2, 3, 4, 5, 6, 7).
~
operator to all __VA_ARGS__
?I have a solution which would do the following:
#define ARRAY_DEFDEC(name, size, ...)
int arr_##name[2*size] = { __VA_ARGS__ };
and then it would be used in the following way:
ARRAY_DEFDEC(test, 7, 1, 2, 3, 4, 5, 6, 7, ~1, ~2, ~3, ~4, ~5, ~6, ~7)
This would require quite a lot of logic to be changed and a user needs to know that besides initialising elements, binary inverse needs to be provided, so I do not really prefer to do this.
At this moment in time I am assuming that argument size has the same size as size(__VA_ARGS__
).
The arrays are intended to be used as a global definition (since they need to be accessed by multiple functions).
Note: Since it is an embedded system, external libraries cannot be included. There is not a single standard library on the system (e.g. stdio, stdarg, stdint, etc). This also further limits options. Standard which is used is C99 and compiler is from Green Hill Software.
Upvotes: 2
Views: 1274
Reputation: 66
I feel like a solution to this would be one of those macros that consists of two dozen sub-macros, and those solutions always make me decide to solve the problem some other way. Macros can do some things, but they're not a full programming language and so they're limited in what they can do.
I would just write a small utility to convert the raw data to C code and then #include that. You can compile the utility as part of your compilation process and then use it to compile the rest of your code. So your data.txt could just say "test 1 2 3 4 5 6 7" and your utility would output whatever declarations you need.
Upvotes: 3
Reputation: 213822
A much better solution than variadic macros is to simply #define
different initializer lists per array.
That is:
#define ARRAY_TEST_7 { 1, 2, 3, 4, 5, 6, 7 }
int arr_array_test[] = ARRAY_TEST_7;
This is common practice. As opposed to inventing your private, project-specific macro language with MAGIC_MACRO(my_variable, stuff);
which is very bad practice.
Optionally add something like:
static_assert(sizeof arr_array_test/sizeof *arr_array_test == expected_size,
"error message");
And to solve inverted values:
#define ARRAY_TEST_7 { 1, 2, 3, 4, 5, 6, 7 }
#define ARRAY_TEST_7_INV { ~1, ~2, ~3, ~4, ~5, ~6, ~7 }
Please note that bitwise inversion of a plain int
probably isn't the best idea for any purpose. (It does not give 2's complement but rather 1's complement) You should very likely use uint32_t
and u
suffix integer constants instead. Or in case you were actually looking for negative numbers, use -
and not ~
.
Alternatively, and as a last resort, you can use "X-macros" to reduce code repetition. They are rather hard to read but at least some manner of industry standard, so that programmers used at reading them can tell what they do. As opposed to secret private macro language which no C programmer in the world knows nor wants to learn.
#include <inttypes.h>
#include <stdio.h>
#define ARRAY3_VALUES(X) \
/* value */ \
X(1) \
X(2) \
X(3) \
#define ARRAY7_VALUES(X) \
/* value */ \
X(1) \
X(2) \
X(3) \
X(4) \
X(5) \
X(6) \
X(7) \
#define ARRAY_ITEM_INIT(val) (val),
#define ARRAY_ITEM_INIT_INV(val) (~(val)),
#define UINT32_ARRAY_DECLARE(size) \
const uint32_t array##size[size] = { ARRAY##size##_VALUES(ARRAY_ITEM_INIT) }
#define UINT32_ARRAY_DECLARE_INV(size) \
const uint32_t array##size##_inv[size] = { ARRAY##size##_VALUES(ARRAY_ITEM_INIT_INV) }
int main (void)
{
UINT32_ARRAY_DECLARE(3); // declares array3 with a given initializer list
UINT32_ARRAY_DECLARE_INV(3); // declares array3_inv with same initializer list but inverted
UINT32_ARRAY_DECLARE(7); // similar but some 7 item version
UINT32_ARRAY_DECLARE_INV(7);
for(size_t i=0; i<3; i++)
printf("%"PRIx32 "%c", array3[i], i==2?'\n':',');
for(size_t i=0; i<3; i++)
printf("%"PRIx32 "%c", array3_inv[i], i==2?'\n':',');
for(size_t i=0; i<7; i++)
printf("%"PRIx32 "%c", array7[i], i==6?'\n':',');
for(size_t i=0; i<7; i++)
printf("%"PRIx32 "%c", array7_inv[i], i==6?'\n':',');
}
Output:
1,2,3
fffffffe,fffffffd,fffffffc
1,2,3,4,5,6,7
fffffffe,fffffffd,fffffffc,fffffffb,fffffffa,fffffff9,fffffff8
Upvotes: 3
Reputation: 141000
Is it possible somehow to apply the ~ operator to all VA_ARGS?
No, you have to overload the macro on number of arguments.
Start with a generic FOREACH macro that allows to apply function f on each argument, here's an example for max up to 3 arguments:
#define FOREACH_1(f, x) f(x)
#define FOREACH_2(f, x, ...) f(x) FOREACH_1(f,__VA_ARGS__)
#define FOREACH_3(f, x, ...) f(x) FOREACH_2(f,__VA_ARGS__)
#define FOREACH_N(_3,_2,_1,N,...) FOREACH_##N
#define FOREACH(f, ...) FOREACH_N(__VA_ARGS__,3,2,1)(f, __VA_ARGS__)
Then you would define the array and define a second one with inverted.
// note the comma
#define INVERT(x) ~x,
#define ARRAY_DEFDEC(name, ...) \
int arr_##name[] = { __VA_ARGS__ }; \
int secondarr_##name[] = { FOREACH(INVERT, __VA_ARGS__) };
ARRAY_DEFDEC(name, 1, 2, 3)
Upvotes: 3