__has_include
是 C++ 17 加進前置處理器(Preprocessor)的新功能。我們能以 __has_include
表達式偵測某個標頭檔(Header File)是否存在。如果前置處理器能找到指定的標頭檔,__has_include
表達式的計算結果會是 1
;反之,其計算結果會是 0
。例如:
#if __has_include("example.h")
#include "example.h"
#else
#error "cannot find example.h"
#endif
int main() {}
因為我們沒有建立 example.h
,所以前置處理器在計算 #if
前置處理指令的時候,會將 __has_include("example.h")
表達式視為 0
,進而觸發第 4 行的 #error
:
$ g++ -std=c++17 example.cpp
example.cpp:4:6: error: #error "cannot find example.h"
#error "cannot find example.h"
^~~~~
如果我們建立 example.h
:
$ touch example.h
則 __has_include("example.h")
會被視為 1
,因此同樣的 example.cpp 就能正常地編譯:
$ g++ -std=c++17 example.cpp
實際應用
一個常見的 __has_include
使用情境是同時支援不同來源的函式庫。舉例來說,如果你的專案要同時支援 C++ 14 與 C++ 17,而且你想要使用 C++ 17 的 std::optional
,你可以使用 __has_include
測試 <optional>
標頭檔。如果找不到 <optional>
則以 <boost/optional/optional.hpp>
替代:
#if defined(__has_include) && __has_include(<optional>)
#include <optional>
using std::optional;
#else
#include <boost/optional/optional.hpp>
using boost::optional;
#endif
int main() {
optional<int> x;
}
以上程式碼中,我們先以 defined(__has_include)
檢查前置處理器是否支援 __has_include
表達式。支援 C++ 17 的編譯器必須回傳 1
。部分編譯器(例如:Clang)在 C++ 17 以前就以語言擴充(Language Extension)的形式支援 __has_include
表達式,所以我們是以 defined(__has_include)
檢查支援度。
接著,我們以 __has_include(<optional>)
偵測 <optional>
標頭檔。如果有找到,則直接使用標準函式庫的 std::optional
,並以 using
宣告將 optional
引入視野(Scope)。如果沒有找到,則使用 Boost 函式庫的 boost::optional
,它與標準函式庫 std::optional
相似,可以作為 std::optional
在 C++ 14 的替代品。
參考資料
- P0061R1: __has_include for C++17
- Clang Language Extensions -- Clang 3.7 documentation
Note
如果你喜歡這篇文章,請追蹤羅根學習筆記粉絲專頁。最新文章都會在粉絲專頁發佈通知。