开发者

Type-generic programming with macros: tricks to determine type?

It's possible to do certain types of type-generic-functions as macros in C, 开发者_StackOverflow社区for instance things like:

#define SQRT(x) (sizeof(x) == sizeof(float) ? sqrtf((x)) : \
                 sizeof(x) == sizeof(double) ? sqrt((x)) : \
                 sqrtl((x)) )

This works (mostly) as expected as long as x is a floating point type.

But what if I want a type-generic macro that can take either an integer type or a pointer type, which might have the same size. Is there a clever way to test whether the macro argument is an integer or a pointer? What about an integer versus a floating point type?


You may detect if an expression is an integer expression or a char* expression, at least on architectures where cast from pointer to uintptr_t is well defined:

#define INT_OR_CHARP(X) (((uintptr_t)((X)+1) - (uintptr_t)(X)) == 1)

This will detect if X is a pointer to a type T with sizeof(T) > 1. This will not work for void* and other corner cases. And because X is evaluated two times you'd have to watch for side effects.

To avoid problems with integer overflow if X is e.g of type signed int you may replace (X) with

(1 ? (X) : (uintmax_t)0)

This guarantees that if X is an integer expression this will be of type uintmax_t. The +1 will then perhaps wrap around, but the result is always well defined and the difference between the two parts will always be 1. If X is a pointer expression, then this is so because any constant integer expression of value 0 is also a null pointer constant.

In total this gives

#define INT_OR_CHARP(X) (((uintptr_t)((1 ? (X) : (uintmax_t)0)+1) - (uintptr_t)(1 ? (X) : (uintmax_t)0)) == 1)


The _Generic keyword was added in the C11 standard for this purpose.

It works like a switch statement for expression types.

Your example can be written using this keyword like this:

#define SQRT(X) _Generic((X), \
    float: sqrtf, \
    double: sqrt, \
    default: sqrtl \
)(X)

GCC provides support for this keyword since version 4.9.


No. Macros do not know what types are. They perform a literal copy-and-paste of the #define. Type safety simply doesn't exist here.

C is not a strongly typed language in any meaningful sense. If you want some modicum of type safety, use C++, where you can accomplish a little with templates and function overloading.


Your result isn't really type-generic, because the result is always long double regardless of what type of argument is passed - the result type of ?: when the second and third operands are arithmetic types is the type that would result from applying the usual arithmetic conversions to those operands. To make it so, you could use GCC's typeof extension:

#define SQRT(x) (__typeof__ (x))(sizeof(x) == sizeof(float) ? sqrtf((x)) : \
                 sizeof(x) == sizeof(double) ? sqrt((x)) : \
                 sqrtl((x)) )

Integer versus floating-point can also be done using typeof:

(__typeof__ (X))1.1 == 1

I can't think of a way to do integer-versus-pointer. The techniques described on this page are quite interesting, though.


It is possible to have some sort of type checking system, but it really is a kludge in C.

glib does this; you can have a look at how they do it, or possibly use it yourself (it's a nifty C library to have around anyway).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜