使用_Generic检测uint_fastN_t/uint_leastN_t遇兼容错误的解决问询
使用
_Generic宏检测uint_fastN_t/uint_leastN_t类型的问题 我尝试用_Generic宏检测所有uint_fastN_t和uint_leastN_t类型,编写了如下代码:
#include <stdint.h> #define IS_UNSIGNED1(T) \ _Generic((T), \ uint_fast8_t : 1, \ uint_fast16_t: 1, \ uint_fast32_t: 1, \ uint_fast64_t: 1, \ default : 0 \ ) #define IS_UNSIGNED(T) \ _Generic((T), \ uint_least8_t : 1, \ uint_least16_t: 1, \ uint_least32_t: 1, \ uint_least64_t: 1, \ default : IS_UNSIGNED1(T) \ ) int main(void) { IS_UNSIGNED((uint_least8_t){0}); }
编译时出现以下错误:
<source>: In function 'main': <source>:7:9: error: '_Generic' specifies two compatible types 7 | uint_fast32_t: 1, \ | ^~~~~~~~~~~~~ <source>:18:25: note: in expansion of macro 'IS_UNSIGNED1' 18 | default : IS_UNSIGNED1(T) \ | ^~~~~~~~~~~~ <source>:23:5: note: in expansion of macro 'IS_UNSIGNED' 23 | IS_UNSIGNED((uint_least8_t){0}); | ^~~~~~~~~~~ <source>:6:9: note: compatible type is here 6 | uint_fast16_t: 1, \ | ^~~~~~~~~~~~~ <source>:18:25: note: in expansion of macro 'IS_UNSIGNED1' 18 | default : IS_UNSIGNED1(T) \ | ^~~~~~~~~~~~ <source>:23:5: note: in expansion of macro 'IS_UNSIGNED' 23 | IS_UNSIGNED((uint_least8_t){0}); | ^~~~~~~~~~~ <source>:8:9: error: '_Generic' specifies two compatible types 8 | uint_fast64_t: 1, \ | ^~~~~~~~~~~~~ <source>:18:25: note: in expansion of macro 'IS_UNSIGNED1' 18 | default : IS_UNSIGNED1(T) \ | ^~~~~~~~~~~~ <source>:23:5: note: in expansion of macro 'IS_UNSIGNED' 23 | IS_UNSIGNED((uint_least8_t){0}); | ^~~~~~~~~~~ <source>:6:9: note: compatible type is here 6 | uint_fast16_t: 1, \ | ^~~~~~~~~~~~~ <source>:18:25: note: in expansion of macro 'IS_UNSIGNED1' 18 | default : IS_UNSIGNED1(T) \ | ^~~~~~~~~~~~ <source>:23:5: note: in expansion of macro 'IS_UNSIGNED' 23 | IS_UNSIGNED((uint_least8_t){0}); | ^~~~~~~~~~~ <source>:8:9: error: '_Generic' specifies two compatible types 8 | uint_fast64_t: 1, \ | ^~~~~~~~~~~~~ <source>:18:25: note: in expansion of macro 'IS_UNSIGNED1' 18 | default : IS_UNSIGNED1(T) \ | ^~~~~~~~~~~~ <source>:23:5: note: in expansion of macro 'IS_UNSIGNED' 23 | IS_UNSIGNED((uint_least8_t){0}); | ^~~~~~~~~~~ <source>:7:9: note: compatible type is here 7 | uint_fast32_t: 1, \ | ^~~~~~~~~~~~~ <source>:18:25: note: in expansion of macro 'IS_UNSIGNED1' 18 | default : IS_UNSIGNED1(T) \ | ^~~~~~~~~~~~ <source>:23:5: note: in expansion of macro 'IS_UNSIGNED' 23 | IS_UNSIGNED((uint_least8_t){0}); | ^~~~~~~~~~~ Compiler returned: 1
错误提示表明uint_leastN_t和uint_fastN_t类型之间可能存在兼容性(C标准并未禁止这种情况)。我想知道是否必须采用8层间接方式来检测这8种类型,还是有更优的解决方案?(目前我已用10层间接检测所有无符号类型,拆分后会增至16层,检测有符号类型还需再加16层。)
解决方案
问题根源在于:_Generic要求每个分支的类型标签必须是互不兼容的类型。嵌套调用时,若uint_leastN_t和uint_fastN_t是同一类型的别名(平台允许这种实现),就会触发兼容类型冲突。以下是两种更优的解决思路:
思路1:合并所有类型到单个_Generic,用类型标签规避兼容问题
通过生成基于类型大小和对齐的唯一指针类型,让兼容类型的分支自动合并,无需分层嵌套:
#include <stdint.h> // 生成唯一的类型标记:利用类型的大小+对齐属性区分 #define TYPE_TAG(T) ((struct { char c[sizeof(T) + __alignof__(T)]; }*)0) #define IS_UNSIGNED_FAST_LEAST(T) \ _Generic(TYPE_TAG(T), \ TYPE_TAG(uint_fast8_t): 1, \ TYPE_TAG(uint_fast16_t): 1, \ TYPE_TAG(uint_fast32_t): 1, \ TYPE_TAG(uint_fast64_t): 1, \ TYPE_TAG(uint_least8_t): 1, \ TYPE_TAG(uint_least16_t): 1, \ TYPE_TAG(uint_least32_t): 1, \ TYPE_TAG(uint_least64_t): 1, \ default: 0 \ ) int main(void) { static_assert(IS_UNSIGNED_FAST_LEAST((uint_least8_t){0}) == 1, ""); static_assert(IS_UNSIGNED_FAST_LEAST((uint_fast32_t){0}) == 1, ""); static_assert(IS_UNSIGNED_FAST_LEAST((int){0}) == 0, ""); return 0; }
原理:TYPE_TAG(T)生成的结构体指针类型,会完全匹配原类型的大小和对齐属性。如果两个类型兼容(比如是同一类型的别名),它们的TYPE_TAG也会是相同的指针类型,不会触发_Generic的兼容错误。
思路2:先过滤无符号类型,再匹配目标类型
如果需求只是判断类型属于无符号的fast/least家族,而非精确匹配某一个,可以拆分检测逻辑:
#include <stdint.h> // 先判断是否为无符号基础类型 #define IS_UNSIGNED_TYPE(T) _Generic((T), \ unsigned char:1, unsigned short:1, unsigned int:1, \ unsigned long:1, unsigned long long:1, default:0) // 再匹配目标的fast/least类型 #define IS_FAST_LEAST_UNSIGNED(T) \ (IS_UNSIGNED_TYPE(T) && \ _Generic(T, \ uint_fast8_t:1, uint_fast16_t:1, uint_fast32_t:1, uint_fast64_t:1, \ uint_least8_t:1, uint_least16_t:1, uint_least32_t:1, uint_least64_t:1, \ default:0 \ ))
优势:代码更易读,同时通过先过滤无符号类型,减少了_Generic分支的冲突概率。
内容的提问来源于stack exchange,提问作者Madagascar
相关产品推荐
相关产品推荐

