Reputation: 483
I've written a set of macros to create and use fifos. Macros allow for a generic, yet still very fast implementation on all systems with static memory allocation, such as in small embedded systems. The guys over at codereview did not have any major concerns with my implementation either.
The data is put into anonymous struts, all data is accessed by the identifier of that struct. Currently the functions-like macros to create these structs look like this
#define _fff_create(_type, _depth, _id) \
struct {uint8_t read; uint8_t write; _type data[_depth];} _id = {0,0,{}}
#define _fff_create_deep(_type, _depth, _id) \
struct {uint16_t read; uint16_t write; _type data[_depth];} _id = {0,0,{}}
Now I'd like to merge both of these into one macro. To do this I've to figure the minimum required size of read
and write
to index _depth
amount of elements at compile time. Parameters name starting with _
indicate only a literal or a #define value might be passed, both are known at compile time.
Thus I hope to find a macro typeof_literal(arg)
which returns uint8_t
if arg<256 or uint16_t
else.
typeof()
. However when used with any literal it returns an int
type, which is two byte on my system.typeof(({uint8_t u8 = 1; u8;}))
will correctly return uint8_t
. However I could not figure out a way to put a condition for the type in that block:typeof(({uint8_t u8 = 1; uint16_t u16 = 1; input ? u8 : u16;}))
always returns uint16_t
because of the type promotion of the ?: operatorif(...)
can't be used either, as any command will happen in "lower" blocks#if
, which make them unusable for this comparison either.I realize there might not be a solution to this problem. That's ok too; the current code is just a minor inconvinience. Yet I'd like to know if there's a tricky way around this. A solution to this could open up new possibilities for macros in general. If you are sure that this can't be possible, please explain why.
Upvotes: 4
Views: 475
Reputation: 483
The macro I was looking for can indeed be written with __builtin_choose_expr
as Florian suggested. My solution is attached below, it has been tested and is confirmed working. Use it as you wish!
#define typeof_literal(_literal) \
typeof(__builtin_choose_expr((_literal)>0, \
__builtin_choose_expr((_literal)<=UINT8_MAX, (uint8_t) 0, \
__builtin_choose_expr((_literal)<=UINT16_MAX, (uint16_t) 0, \
__builtin_choose_expr((_literal)<=UINT32_MAX, (uint32_t) 0, (uint64_t) 0))), \
__builtin_choose_expr((_literal)>=INT8_MIN, (int8_t) 0, \
__builtin_choose_expr((_literal)>=INT16_MIN, (int16_t) 0, \
__builtin_choose_expr((_literal)>=INT32_MIN, (int32_t) 0, (int64_t) 0)))))
Upvotes: 1
Reputation: 33719
I think the building block you are looking for is __builtin_choose_expr
, which is a lot like the ternary operator, but does not convert its result to a common type. With
#define CHOICE(x) __builtin_choose_expr (x, (int) 1, (short) 2)
this
printf ("%zu %zu\n", sizeof (CHOICE (0)), sizeof (CHOICE (1)));
will print
2 4
as expected.
However, as Greg Hewgill points out, C++ has better facilities for that (but they are still difficult to use).
Upvotes: 1