Is is possible to define a type-generic macro that supports all standard (unsigned) integer types, and also size_t
?
foo((size_t)42)
works with the example below as long as size_t
refers to one of the standard unsigned integer types. But does the standard guarantee anywhere that this is the case, or could size_t
also refer to an extended integer type?
void foo_bool(_Bool x);
void foo_uchar(unsigned char x);
void foo_ushort(unsigned short x);
void foo_uint(unsigned x);
void foo_ulong(unsigned long x);
void foo_ullong(unsigned long long x);
#define foo(x) _Generic((x), \
_Bool: foo_bool, \
unsigned char: foo_uchar, \
unsigned short: foo_ushort, \
unsigned: foo_uint, \
unsigned long: foo_ulong, \
unsigned long long: foo_ullong)(x)
Is is possible to define a type-generic macro that supports all standard (unsigned) integer types, and also
size_t
?
Yes - with nesting.
size_t
commonly matches a standard integer type like unsigned, unsigned long, unsigned long long
, yet that is not required by C. The trick becomes how to use _Generic
when size_t
is the same as a standard type and when it is not a standard type. A similar issue applies to uintmax_t
.
void foo_size_t(size_t x) { (void)x; puts("size_t"); }
void foo_bool(_Bool x) { (void)x; puts("bool"); }
void foo_uchar(unsigned char x){ (void)x; puts("unsigned char"); }
void foo_ushort(unsigned short x){ (void)x; puts("unsigned short"); }
void foo_uint(unsigned x) { (void)x; puts("unsigned"); }
void foo_ulong(unsigned long x){ (void)x; puts("unsigned long"); }
void foo_ullong(unsigned long long x){ (void)x; puts("unsigned long long"); }
void foo_none(){ puts("TBD"); }
#define foo(x) _Generic((x), \
_Bool: foo_bool, \
unsigned char: foo_uchar, \
unsigned short: foo_ushort, \
unsigned: foo_uint, \
unsigned long: foo_ulong, \
unsigned long long: foo_ullong, \
default: foo_none)(x)
// Catch `size_t` as `size_t`
// If `size_t` is distinctive, apply the size_t function
// If `size_t` is not distinctive, others will be caught here too
#define bar(x) _Generic((x), \
size_t: foo_size_t(x), \
default: foo(x) \
)
int main(void) {
foo((float)1); // What happens when type matching nothing
foo(1lu);
foo((size_t)1);
bar((float)1); // What happens when type matching nothing
bar((size_t)1);
bar(1lu);
}
Output
TBD
unsigned long
unsigned long // Overlap when size_t matches a type, else TBD
TBD
size_t // expect this as unsigned long if size_t was distinctive.
size_t
Note that creative uses of _Generic
may lead to issues as this expansion to C is still nascent.
I don't know of any implementation that uses a non-standard type for this.
If it is really only size_t
you are worried about:
foo_size
function and put it in a default
clause.size_t
is not among
the standard unsigned types. Something like
static_assert(
_Generic(sizeof(int),
unsigned char: 1,
unsigned short: 1,
...
unsigned long long, 1,
default: 0),
"size_t not among the standard unsigned types");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With