有没有办法在一个翻译单元中理智地使用 GCC __attribute__((noreturn)) 和 <stdnoreturn.h>?

Is there a way to use GCC __attribute__((noreturn)) and <stdnoreturn.h> sanely in a single translation unit?

在 C11 中,有一个关键字 _Noreturn,它是一个函数说明符(就像 inline 一样),表示函数不 return — 它调用 exit()或同等学历。还有一个header,<stdnoreturn.h>,完整的定义是:

7.23 _Noreturn <stdnoreturn.h>

¶1 The header <stdnoreturn.h> defines the macro

noreturn

which expands to _Noreturn.

在 GNU C (GCC) 中,有一个属性 __attribute__((noreturn)) 基本上完成相同的工作。一个区别是 __attribute__((noreturn)) 可以出现在函数声明符之前或之后:

extern __attribute__((noreturn)) void err_error(const char *format, ...);
extern void err_error(const char *format, ...) __attribute__((noreturn));

问题是,如果您在翻译单元 (TU) 中包含 <stdnoreturn.h>,任何后续使用的 __attribute__((noreturn)) 都会映射到 __attribute__((_Noreturn)),这会导致编译失败,因为属性 _Noreturn 未被识别(即使关键字被识别为关键字)。也就是说,据我所知,您不能同时使用这两个系统。

您可以通过测试 __STDC_VERSION__ >= 201112L 来检测 C11 — 该值由技术勘误 1 指定,因为标准意外地留下了不完整指定的值:

__STDC_VERSION__ The integer constant 201ymmL.178)

178) This macro was not specified in ISO/IEC 9899:1990 and was specified as 199409L in ISO/IEC 9899:1990/Amd.1:1995 and as 199901L in ISO/IEC 9899:1999. The intention is that this will remain an integer constant of type long int that is increased with each revision of this International Standard.

这可用于在任何一个 header 文件中对处理进行条件处理,但如果您混合使用修改过的和未修改的(C11 和 C99)headers,我似乎是 运行 变成 <stdnoreturn.h> 的棘手问题。

改用__attribute__((__noreturn__))

所有 gcc 属性都有一个 __ 版本,这样您就可以在系统头文件中合理地使用它们而不会污染用户名 space。

一些平台提供商似乎也没有意识到这个问题。我最近发现,对于 OS X,他们在宏 __dead2.

中有未受保护的版本

此外,您无法使用 __STDC_VERSION__ 宏可靠地检测 C 标准版本。所有具有 C11 的 "test" 版本的 gcc 和 clang 版本如果使用 -std=c11 调用则声明 C11 而没有完全实现它。较新的版本几乎已经存在,但只是差不多。

这正是关键字是 _Noreturn 而不是 noreturn 的原因。不要包含 stdnoreturn.h header,只需使用:

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define noreturn _Noreturn
#elif defined(__GNUC__)
#define noreturn __attribute__((__noreturn__))
#else
#define noreturn
#endif