nqtronix
nqtronix

Reputation: 483

C Macro: get smallest type for an integer constant

Why I need to figure out the smalles type of a literal (Backstory)

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,{}}

What I'm looking for

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.

What I've tried

  1. GCC 4.9.2. offers a command called typeof(). However when used with any literal it returns an int type, which is two byte on my system.
  2. Another feature of GCC 4.9.2 is a compound statement. 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:
  3. typeof(({uint8_t u8 = 1; uint16_t u16 = 1; input ? u8 : u16;})) always returns uint16_t because of the type promotion of the ?: operator
  4. if(...) can't be used either, as any command will happen in "lower" blocks
  5. Macros can't contain #if, which make them unusable for this comparison either.

Can't you just leave it like that?

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

Answers (2)

nqtronix
nqtronix

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

Florian Weimer
Florian Weimer

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

Related Questions