1

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?

2
  • Isn't the whole point that you get distinct types? I haven't played around with this yet but I picture it as a wrapper for struct bit fields. And there too, every struct with a member int x : n is a distinct type as well.
    – Lundin
    Commented Jun 10 at 6:46
  • @Lundin The complaint is not that the types are distinct. I am looking to add _BitInt and unsigned _BitInt to the type traits IS_SIGNED() and IS_UNSIGNED() and wondering how to without having separate cases from 2 to BITINT_MAXWIDTH.
    – Harith
    Commented Jun 10 at 6:48

1 Answer 1

2

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.

Not the answer you're looking for? Browse other questions tagged or ask your own question.