首先,我想说丹尼尔弗雷的回答是绝对正确的;你真的应该使用__has_feature
,__has_extension
等。若有可能。Clang 语言扩展页面记录了您可以检查的不同内容,这应该是您的首选解决方案。
也就是说,有时您确实需要检查版本。例如,有时需要解决已在较新版本中修复或仅在较新版本中出现的编译器错误。有时会添加新功能;例如,在 clang 9 之前,该属性__builtin_constant_p
无法正常工作diagnose_if
。有时添加了一个功能但没有相应的检查。
我真的希望 clang 将上游版本号公开为预处理器宏,这样我们就可以可靠地处理这样的情况,但他们没有。您可以手动创建 Apple 版本号到上游的地图,这是其他几个答案所建议的,但这有一些非常明显的缺点。对我来说,致命的缺陷是它不适用于 Apple clang 以外的编译器。现在有很多基于 clang 的编译器(IBM XL C/C++、一些较新的 PGI/NVIDIA 编译器、下一代 Intel C/C++ 等)。
我的解决方法是使用特征检测宏来估计版本号。例如,-Wimplicit-const-int-float-conversion
在 clang 11 中添加了,所以如果__has_warning("-Wimplicit-const-int-float-conversion")
为真,我们可以假设上游 clang 版本 >= 11。类似地,添加了 clang 10 -Wmisleading-indentation
,clang 9 开始定义__FILE_NAME__
预处理器宏,等等。
我创建了一个包含必要逻辑的小标题。它是公共领域 (CC0),即使它是我的一个项目 ( SIMDe ) 的一部分,它也不依赖于任何其他文件中的任何其他内容,因此您可以为自己的项目随意窃取它,而无需复制所有文件辛德。
显然,该文件需要对每个版本的 clang 进行新测试,因此如果您需要能够检查更新的编译器,它确实需要偶尔更新,所以我建议从 SIMDe git 存储库中获取最新版本(我不是可能会保持这个答案是最新的),但这是检查现在的样子:
#if defined(__clang__) && !defined(SIMDE_DETECT_CLANG_VERSION)
# if __has_warning("-Wformat-insufficient-args")
# define SIMDE_DETECT_CLANG_VERSION 120000
# elif __has_warning("-Wimplicit-const-int-float-conversion")
# define SIMDE_DETECT_CLANG_VERSION 110000
# elif __has_warning("-Wmisleading-indentation")
# define SIMDE_DETECT_CLANG_VERSION 100000
# elif defined(__FILE_NAME__)
# define SIMDE_DETECT_CLANG_VERSION 90000
# elif __has_warning("-Wextra-semi-stmt") || __has_builtin(__builtin_rotateleft32)
# define SIMDE_DETECT_CLANG_VERSION 80000
# elif __has_warning("-Wc++98-compat-extra-semi")
# define SIMDE_DETECT_CLANG_VERSION 70000
# elif __has_warning("-Wpragma-pack")
# define SIMDE_DETECT_CLANG_VERSION 60000
# elif __has_warning("-Wbitfield-enum-conversion")
# define SIMDE_DETECT_CLANG_VERSION 50000
# elif __has_attribute(diagnose_if)
# define SIMDE_DETECT_CLANG_VERSION 40000
# elif __has_warning("-Wcomma")
# define SIMDE_DETECT_CLANG_VERSION 39000
# elif __has_warning("-Wdouble-promotion")
# define SIMDE_DETECT_CLANG_VERSION 38000
# elif __has_warning("-Wshift-negative-value")
# define SIMDE_DETECT_CLANG_VERSION 37000
# elif __has_warning("-Wambiguous-ellipsis")
# define SIMDE_DETECT_CLANG_VERSION 36000
# else
# define SIMDE_DETECT_CLANG_VERSION 1
# endif
#endif /* defined(__clang__) && !defined(SIMDE_DETECT_CLANG_VERSION) */
我认为这种方法的最大问题实际上与我所知道的所有其他检测上游 clang 版本的尝试共享:不一定有对应于相关代码的 clang 版本。据我所知,大多数基于 clang 的编译器实际上并不是基于版本,而是一些随机提交(可能是他们想要基于其工作的分支的最新提交)。这意味着,例如,如果某个问题在 clang $N 开发周期的后期得到修复,Apple 的分叉通常可能与 clang $N 相同,但不包含错误修复。相反,也许 Apple 会从 clang $N+1 移植一个修复程序,而 clang $N 中存在的错误将在 Apple 的版本中得到修复。