使用宏的类型通用编程:确定类型的技巧?

发布于 2024-10-05 17:59:34 字数 370 浏览 0 评论 0原文

C中可以将某些类型的类型通用函数作为宏来执行,例如:

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

只要x是一个浮点数,这(大部分)就可以按预期工作点类型。

但是,如果我想要一个可以采用整数类型或指针类型(它们可能具有相同大小)的类型通用宏,该怎么办?有没有一种聪明的方法来测试宏参数是整数还是指针?整数与浮点类型怎么样?

It's possible to do certain types of type-generic-functions as macros in C, 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

手心的海 2024-10-12 17:59:34

您可以检测表达式是整数表达式还是 char* 表达式,至少在从指针到 uintptr_t 的转换定义明确的架构上:

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

这将检测是否>X 是一个指向 T 类型的指针,其中 sizeof(T) > 1..这不适用于 void* 和其他极端情况。由于 X 会被评估两次,因此您必须注意副作用。

为了避免整数溢出问题,如果 Xsigned int 类型,您可以将 (X) 替换为

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

这保证了如果 X 是一个整数表达式,其类型为 uintmax_t。然后,+1 可能会回绕,但结果始终是明确定义的,并且两部分之间的差异始终是 1。如果X是一个指针表达式,那么这是因为任何值为0的常量整数表达式也是一个空指针常量

总共这给出了

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

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)
九局 2024-10-12 17:59:34

_Generic 关键字已添加到 C11 标准中为此目的。

它的工作原理类似于表达式类型的 switch 语句。

您的示例可以使用此关键字编写,如下所示:

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

GCC 自 版本 4.9 起提供对此关键字的支持。

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.

动次打次papapa 2024-10-12 17:59:34

不。宏不知道什么是类型。他们执行 #define 的字面复制和粘贴。这里根本不存在类型安全。

从任何意义上来说,C 都不是强类型语言。如果您想要一定程度的类型安全,请使用 C++,您可以通过模板和函数重载完成一些工作。

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.

枯寂 2024-10-12 17:59:34

您的结果并不是真正类型通用的,因为无论传递什么类型的参数,结果始终是long double - ?:< 的结果类型/code> 当第二个和第三个操作数是算术类型时,是对这些操作数应用常规算术转换所产生的类型。为此,您可以使用 GCC 的 typeof 扩展:

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

整数与浮点也可以使用 typeof 完成:

(__typeof__ (X))1.1 == 1

我想不出一种方法来执行整数 -与指针。 此页面 不过很有趣。

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.

国际总奸 2024-10-12 17:59:34

可以有某种类型检查系统,但它实际上是 C 中的一个拼凑。

glib 就是这样做的;你可以看看他们是如何做到的,或者你自己也可以使用它(无论如何,这是一个漂亮的 C 库)。

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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文