Madagascar
Madagascar

Reputation: 7345

Detecting _BitInt type in _Generic

From ISO/IEC 9899:2024, 6.2.5 Types:

... 5 A bit-precise signed integer type is designated as _BitInt(N) where N is an integer constant expression that specifies the number of bits that are used to represent the type, including the sign bit. Each value of N designates a distinct type. (30)

where the footnote states:

  1. Thus, _BitInt(3) is not the same type as _BitInt(4).

So I put together this code:

#include <assert.h>

#define IS_SIGNED(T) \
    _Generic((T),    \
        _BitInt: 1,  \
        default: 0   \
    )

#define IS_UNSIGNED(T)       \
    _Generic((T),            \
        unsigned _BitInt: 1, \
        default         : 0  \
    )

int main(void)
{
    _BitInt(10) ten_bits = 0;
    _BitInt(5) five_bits = 0;
    unsigned _BitInt(2) two_bits = 0;
    unsigned _BitInt(8) eight_bits = 0;

    assert(IS_SIGNED(ten_bits));
    assert(!IS_SIGNED(five_bits));
    
    assert(IS_UNSIGNED(two_bits));    
    assert(!IS_UNSIGNED(eight_bits));
}

which did not compile, of course, because _BitInt is meaningless by itself, and requires an integer constant expression between parenthesis. Changing the cases in _Generic to:

    _BitInt(10): 1,

and:

    unsigned _BitInt(2): 1,

sure worked. Now is there a way I can determine whether an expression has type _BitInt(N) or type unsigned _BitInt(N) where N is any integer constant expression without having separate cases from 2 to BITINT_MAXWIDTH?

Upvotes: 1

Views: 144

Answers (1)

Lundin
Lundin

Reputation: 214770

I don't think you can do this with _Generic. A compromise might be to do something like this instead:

#define IS_SIGNED(T)   ( ~(typeof(T)){} <  0)
#define IS_UNSIGNED(T) ( ~(typeof(T)){} >= 0)

This creates a compound literal of the same type as T (which may be _BitInt or some other integer type), initializes it to zero and does a bitwise inverse. Since the operand 0 of the comparison operators is of type int, there may be an integer promotion of the operand containing the compound literal. However, in case it was signed it will still be negative and the sign is preserved and in case it was unsigned, it will remain a positive value after promotion.

The down side is that this isn't really type safe. You could pass a large integer type, a float type or a pointer and then the macro could fail.

Upvotes: 2

Related Questions