【问题标题】:Dead branch of _Generic expression causing compiler error (C11)_Generic 表达式的死分支导致编译器错误 (C11)
【发布时间】:2023-03-12 09:17:01
【问题描述】:

我有兴趣编写一个类型验证宏,它只会在类型不是 int/short/long 或 pointer 时发出警告。

我遇到的问题是指针可以是任何类型的指针。

#define INT_OR_POINTER_AS_UINTPTR(v) _Generic((v), \
    signed long:  (uintptr_t)(v),            unsigned long:  (uintptr_t)(v), \
    signed int:   (uintptr_t)(v),            unsigned int:   (uintptr_t)(v), \
    signed short: (uintptr_t)(v),            unsigned short: (uintptr_t)(v), \
    default: (((void)(0 ? (*(v)) : 0),                       (uintptr_t)(v))))

第一块是允许int/short/long

default 的情况是允许任何指针。

(0 ? (*(v)) : 0) 的目的是在v 不是指针时导致编译器错误,否则不会影响生成的代码(请注意0 ? ...)。

这样,来自其他类型(例如 floatbool)的意外隐式转换不会被忽视。

理想情况下,这是可行的。

int a = 4;
struct Foo *b = NULL;

uintptr_t test_a = INT_OR_POINTER_AS_UINTPTR(a);
uintptr_t test_b = INT_OR_POINTER_AS_UINTPTR(b);

理想情况下,这两种用途都会失败。

float a = 4;
struct Foo b = {0};

uintptr_t test_a = INT_OR_POINTER_AS_UINTPTR(a);
uintptr_t test_b = INT_OR_POINTER_AS_UINTPTR(b);

但是,即使将 int/long/short 作为参数给出,检查指针的代码也会被评估并出现错误:invalid type argument of unary '*' (have 'int')

不必显式枚举可能传递给此_Generic 的每种指针类型,有没有一种方法可以捕获所有类型的指针,而无需评估其他(非指针)值的表达式?

【问题讨论】:

标签: c generics c11


【解决方案1】:

_Bool 是“标准无符号整数类型”,见 6.2.5,所以如果你想对此进行特殊处理,你需要单独进行。

否则,您可以使用一个简单的技巧。整数和指针具有对称差是整数的性质。所以我们在只能接受整数的上下文中使用对称差分,比如数组下标:

#define INT_OR_POINTER_AS_UINTPTR(v) \
             ((sizeof "Allow only integer and pointer types"[(v)-(v)]), (uintptr_t)(v))

struct 类型的减法会失败,浮点类型会产生浮点差,不能用于指针运算。

要拒绝_Bool,您可以使用_Generic

【讨论】:

  • 这有一个缺点是要评估v 3 次,这是一个“不不不!”对于宏。您可以将第一部分打包到 sizeof 中以避免这种情况。
  • @Jens:好主意(原始宏还多次评估了它的参数)
猜你喜欢
  • 1970-01-01
  • 2012-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-11
  • 1970-01-01
  • 2017-01-06
  • 2017-06-30
相关资源
最近更新 更多