首先,我想说Daniel Frey的回答绝对是正确的;尽可能使用
__has_feature
,
__has_extension
等。
Clang语言扩展页面记录了您可以检查的不同内容,这应该是您的首选解决方案。
话虽如此,有时确实需要检查版本。例如,有时需要解决在较新版本中已修复或仅出现在较新版本中的编译器错误。有时会添加新功能;例如,在clang 9之前,
__builtin_constant_p
与 diagnose_if
属性一起使用无法正常工作。有时添加功能但没有相应的检查。
我真希望Clang能够将上游版本号公开为预处理器宏,这样我们就可以可靠地处理类似的情况,但他们没有。您可以手动创建一个苹果版本号到上游版本号的映射表,这是其他一些答案提出的,但它有一些非常明显的缺点。对我来说,致命的缺陷是它并不适用于除苹果Clang之外的编译器;现在有很多基于Clang的编译器(IBM XL C/C ++、一些新的PGI / NVIDIA编译器、下一代英特尔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)的一部分,但它不依赖于任何其他文件,因此您可以自由地将其用于自己的项目中,而无需复制整个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
我认为这种方法最大的问题实际上与我所知道的所有其他检测上游clang版本的尝试都存在共同之处:并不一定有一个对应于所涉及代码的clang版本。据我所知,大多数基于clang的编译器实际上并不是基于发布版,而是某个随机提交(可能是他们想要基于其工作的分支的最新提交)。这意味着,例如,如果在clang $N开发周期后期修复了问题,则苹果的分支通常与clang $N相同,但不包含错误修复。相反,也许苹果会从clang $N+1中回溯修复,并且在clang $N中存在的错误将在苹果的版本中得到修复。