WriteCompilerDetectionHeader

自版本 3.20 棄用:只有在策略 CMP0120 未設定為 NEW 時,此模組才可用。請勿在新程式碼中使用它。

在版本 3.1 中新增。

此模組提供函式 write_compiler_detection_header()

此函式可用於產生適用於前處理器包含的檔案,其中包含要在原始碼中使用的巨集

write_compiler_detection_header(
          FILE <file>
          PREFIX <prefix>
          [OUTPUT_FILES_VAR <output_files_var> OUTPUT_DIR <output_dir>]
          COMPILERS <compiler> [...]
          FEATURES <feature> [...]
          [BARE_FEATURES <feature> [...]]
          [VERSION <version>]
          [PROLOG <prolog>]
          [EPILOG <epilog>]
          [ALLOW_UNKNOWN_COMPILERS]
          [ALLOW_UNKNOWN_COMPILER_VERSIONS]
)

這會產生檔案 <file>,其中包含所有具有字首 <prefix> 的巨集。

預設情況下,所有內容都會直接寫入 <file>。可以指定 OUTPUT_FILES_VAR 以將編譯器特定的內容寫入到個別的檔案中。然後,這些個別的檔案在 <output_files_var> 中可用,並且可以被呼叫者使用,例如用於安裝。OUTPUT_DIR 指定從主要的 <file> 到編譯器特定檔案的相對路徑。例如

write_compiler_detection_header(
  FILE climbingstats_compiler_detection.h
  PREFIX ClimbingStats
  OUTPUT_FILES_VAR support_files
  OUTPUT_DIR compilers
  COMPILERS GNU Clang MSVC Intel
  FEATURES cxx_variadic_templates
)
install(FILES
  ${CMAKE_CURRENT_BINARY_DIR}/climbingstats_compiler_detection.h
  DESTINATION include
)
install(FILES
  ${support_files}
  DESTINATION include/compilers
)

VERSION 可用於指定要產生的 API 版本。CMake 的未來版本可能會引入替代的 API。給定的 API 由任何大於或等於引入給定 API 的 CMake 版本且小於引入其後繼 API 的 CMake 版本的 <version> 值選定。如果未明確指定版本,則使用變數 CMAKE_MINIMUM_REQUIRED_VERSION 的值。(截至 CMake 版本 4.0.0,只有一個 API 版本。)

可以將 PROLOG 指定為要在標頭開頭寫入的文字內容。EPILOG 可以指定為要在標頭結尾寫入的文字內容

必須列出至少一個 <compiler> 和一個 <feature>。CMake 已知的但未指定的編譯器會被偵測到,並為它們產生前處理器 #error。為 CMake 已知的每個編譯器產生一個與 <PREFIX>_COMPILER_IS_<compiler> 相符的前處理器巨集,以包含值 01

可能的編譯器識別碼在 CMAKE_<LANG>_COMPILER_ID 變數中有文件記錄。此 CMake 版本中可用的功能在 CMAKE_C_KNOWN_FEATURESCMAKE_CXX_KNOWN_FEATURES 全域屬性中列出。請參閱 cmake-compile-features(7) 手冊,以取得有關編譯功能的資訊。

在版本 3.2 中新增:新增 MSVCAppleClang 編譯器支援。

在版本 3.6 中新增:新增 Intel 編譯器支援。

在版本 3.8 中變更:如果要求 {c,cxx}_std_* 元功能,則會忽略它們。

在版本 3.8 中新增:ALLOW_UNKNOWN_COMPILERSALLOW_UNKNOWN_COMPILER_VERSIONS 導致模組產生將未知編譯器視為僅缺少所有功能的條件。如果沒有這些選項,預設行為是為未知的編譯器和版本產生 #error

在版本 3.12 中新增:BARE_FEATURES 將使用較新語言標準中使用的名稱定義相容性巨集,以便程式碼可以無條件地使用新功能名稱。

功能測試巨集

對於每個編譯器,都會產生一個與 <PREFIX>_COMPILER_IS_<compiler> 相符的前處理器巨集,其內容為 01,具體取決於使用的編譯器。如果已定義,則會產生與 <PREFIX>_COMPILER_VERSION_MAJOR <PREFIX>_COMPILER_VERSION_MINOR<PREFIX>_COMPILER_VERSION_PATCH 相符的編譯器版本組件的前處理器巨集,其中包含對應編譯器版本組件的十進制值。

會根據編譯器版本產生前處理器測試,以表示是否啟用每個功能。產生一個與 <PREFIX>_COMPILER_<FEATURE> 相符的前處理器巨集,其中 <FEATURE> 是大寫的 <feature> 名稱,以包含值 01,具體取決於使用的編譯器是否支援該功能

write_compiler_detection_header(
  FILE climbingstats_compiler_detection.h
  PREFIX ClimbingStats
  COMPILERS GNU Clang AppleClang MSVC Intel
  FEATURES cxx_variadic_templates
)
#if ClimbingStats_COMPILER_CXX_VARIADIC_TEMPLATES
template<typename... T>
void someInterface(T t...) { /* ... */ }
#else
// Compatibility versions
template<typename T1>
void someInterface(T1 t1) { /* ... */ }
template<typename T1, typename T2>
void someInterface(T1 t1, T2 t2) { /* ... */ }
template<typename T1, typename T2, typename T3>
void someInterface(T1 t1, T2 t2, T3 t3) { /* ... */ }
#endif

符號巨集

為特定功能建立了一些額外的符號定義,以用作可以有條件地定義為空的符號

class MyClass ClimbingStats_FINAL
{
    ClimbingStats_CONSTEXPR int someInterface() { return 42; }
};

如果編譯器(及其旗標)支援 cxx_final 功能,則 ClimbingStats_FINAL 巨集將展開為 final,如果支援 cxx_constexpr,則 ClimbingStats_CONSTEXPR 巨集將展開為 constexpr

如果將 BARE_FEATURES cxx_final 作為引數給定,則也會為舊編譯器定義 final 關鍵字。

以下功能產生對應的符號定義,如果它們作為 BARE_FEATURES 可用

功能

定義

符號

c_restrict

<PREFIX>_RESTRICT

restrict

cxx_constexpr

<PREFIX>_CONSTEXPR

constexpr

cxx_deleted_functions

<PREFIX>_DELETED_FUNCTION

= delete

cxx_extern_templates

<PREFIX>_EXTERN_TEMPLATE

extern

cxx_final

<PREFIX>_FINAL

final

cxx_noexcept

<PREFIX>_NOEXCEPT

noexcept

cxx_noexcept

<PREFIX>_NOEXCEPT_EXPR(X)

noexcept(X)

cxx_override

<PREFIX>_OVERRIDE

override

相容性實作巨集

如果編譯器不支援某些功能,則適合將這些功能包裝在具有向後相容性實作的巨集中。

當編譯器未提供 cxx_static_assert 功能時,可以透過 <PREFIX>_STATIC_ASSERT(COND)<PREFIX>_STATIC_ASSERT_MSG(COND, MSG) 函式型巨集取得相容性實作。巨集會展開為 static_assert(如果該編譯器功能可用),否則展開為相容性實作。在第一種形式中,條件會在 static_assert 的訊息欄位中字串化。在第二種形式中,訊息 MSG 會傳遞到 static_assert 的訊息欄位,如果使用向後相容性實作,則會忽略該訊息。

cxx_attribute_deprecated 功能提供巨集定義 <PREFIX>_DEPRECATED,它會展開為標準 [[deprecated]] 屬性或編譯器特定的裝飾器,例如 GNU 編譯器使用的 __attribute__((__deprecated__))

cxx_alignas 功能提供巨集定義 <PREFIX>_ALIGNAS,它會展開為標準 alignas 裝飾器或編譯器特定的裝飾器,例如 GNU 編譯器使用的 __attribute__ ((__aligned__))

cxx_alignof 功能提供巨集定義 <PREFIX>_ALIGNOF,它會展開為標準 alignof 裝飾器或編譯器特定的裝飾器,例如 GNU 編譯器使用的 __alignof__

功能

定義

符號

cxx_alignas

<PREFIX>_ALIGNAS

alignas

cxx_alignof

<PREFIX>_ALIGNOF

alignof

cxx_nullptr

<PREFIX>_NULLPTR

nullptr

cxx_static_assert

<PREFIX>_STATIC_ASSERT

static_assert

cxx_static_assert

<PREFIX>_STATIC_ASSERT_MSG

static_assert

cxx_attribute_deprecated

<PREFIX>_DEPRECATED

[[deprecated]]

cxx_attribute_deprecated

<PREFIX>_DEPRECATED_MSG

[[deprecated]]

cxx_thread_local

<PREFIX>_THREAD_LOCAL

thread_local

在整個程式庫中棄用時,會出現使用此類棄用巨集的情況。在這種情況下,程式庫中的所有公共 API 都可以使用 <PREFIX>_DEPRECATED 巨集裝飾。這會在建置程式庫本身時產生非常雜亂的建置輸出,因此在建置已棄用的程式庫時,可以將巨集定義為空

add_library(compat_support ${srcs})
target_compile_definitions(compat_support
  PRIVATE
    CompatSupport_DEPRECATED=
)

範例用法

注意

本節從 cmake-compile-features(7) 手冊遷移而來,因為它依賴於策略 CMP0120 移除的 WriteCompilerDetectionHeader 模組。

如果編譯功能可用,則可能會優先使用它們,而不會產生硬性要求。例如,程式庫可能會根據 cxx_variadic_templates 功能是否可用來提供替代的實作

#if Foo_COMPILER_CXX_VARIADIC_TEMPLATES
template<int I, int... Is>
struct Interface;

template<int I>
struct Interface<I>
{
  static int accumulate()
  {
    return I;
  }
};

template<int I, int... Is>
struct Interface
{
  static int accumulate()
  {
    return I + Interface<Is...>::accumulate();
  }
};
#else
template<int I1, int I2 = 0, int I3 = 0, int I4 = 0>
struct Interface
{
  static int accumulate() { return I1 + I2 + I3 + I4; }
};
#endif

此類介面取決於使用正確的前處理器定義來定義編譯器功能。CMake 可以使用 WriteCompilerDetectionHeader 模組產生包含此類定義的標頭檔。該模組包含 write_compiler_detection_header 函式,該函式接受參數以控制產生的標頭檔的內容

write_compiler_detection_header(
  FILE "${CMAKE_CURRENT_BINARY_DIR}/foo_compiler_detection.h"
  PREFIX Foo
  COMPILERS GNU
  FEATURES
    cxx_variadic_templates
)

此類標頭檔可以在專案的原始碼中內部使用,並且可以安裝並在程式庫程式碼的介面中使用。

對於 FEATURES 中列出的每個功能,都會在標頭檔中建立前處理器定義,並將其定義為 10

此外,某些功能需要額外的定義,例如 cxx_finalcxx_override 功能。 final 關鍵字不是在 #ifdef 程式碼中使用,而是由符號抽象化,該符號定義為 final、編譯器特定的等效符號或為空。這樣,C++ 程式碼可以編寫為無條件地使用該符號,而編譯器支援決定了它展開的內容

struct Interface {
  virtual void Execute() = 0;
};

struct Concrete Foo_FINAL {
  void Execute() Foo_OVERRIDE;
};

在這種情況下,如果編譯器支援關鍵字,Foo_FINAL 將展開為 final,否則展開為空。

在此用例中,專案程式碼可能希望在編譯器提供的情況下啟用特定的語言標準。可以為特定目標將 CXX_STANDARD 目標屬性設定為所需的語言標準,並且可以設定 CMAKE_CXX_STANDARD 變數以影響所有後續目標

write_compiler_detection_header(
  FILE "${CMAKE_CURRENT_BINARY_DIR}/foo_compiler_detection.h"
  PREFIX Foo
  COMPILERS GNU
  FEATURES
    cxx_final cxx_override
)

# Includes foo_compiler_detection.h and uses the Foo_FINAL symbol
# which will expand to 'final' if the compiler supports the requested
# CXX_STANDARD.
add_library(foo foo.cpp)
set_property(TARGET foo PROPERTY CXX_STANDARD 11)

# Includes foo_compiler_detection.h and uses the Foo_FINAL symbol
# which will expand to 'final' if the compiler supports the feature,
# even though CXX_STANDARD is not set explicitly.  The requirement of
# cxx_constexpr causes CMake to set CXX_STANDARD internally, which
# affects the compile flags.
add_library(foo_impl foo_impl.cpp)
target_compile_features(foo_impl PRIVATE cxx_constexpr)

write_compiler_detection_header 函式還為其他具有標準等效功能的功能建立相容性程式碼。例如,cxx_static_assert 功能會使用範本模擬,並透過 <PREFIX>_STATIC_ASSERT<PREFIX>_STATIC_ASSERT_MSG 函式巨集進行抽象化。