在寫shader時經常會出現一些預編譯指令比如 #if #ifdef #if defined 等,有的時候會感到困惑,所以決定仔細查一下具體的使用方法,并做一個簡單的測試。
#define
#define 指令用于定義一個宏或者常量
#define 有兩種常用的格式:
- #define identifier token-string 定義一個宏,在預編譯時會把所有identifier替換成token-string,可以用更容易理解的名稱去代替一個常量,例如:#define PI 3.1415926
- #define identifier( argument0, ..., argumentN-1 ) token-string 定義一個類似函數一樣的宏,例如:#define AREA(area, w, h) (area = w*h);
// #define 的使用 //
#define SOME_MACRO
#define PI 3.141593
定義宏時使用第一種方式,當沒有指定 token-string 時,需要注意的兩點是:
- 這個identifier依然是被定義了的,且可以使用#if defined 或 #ifdef 檢測到的
- 所有identifier字符都會被移除,或者理解為所有identifier的地方都被替換成空字符串。
詳細信息可以參考 #define文檔
#if
#if 是一個預處理指令,用來控制源文件中哪一部分會被編譯。
格式是: #if condition
簡單來說就是 #if 后面的條件語句(condition)如果執行結果不為0,則該#if語句塊內的代碼會被編譯,否則就不會被編譯。
#elif 和 #else 可以類比為常規的用于判斷條件的關鍵字 else if 和 else,區別是前面加了個#符號,用以表明該指令是在預處理階段執行,而不是運行時執行。最后,在所有判斷結束后需要用 #endif 來作為結尾,用于確定預處理語句的作用范圍。
// #if 的使用 //
#define SOME_MACRO 0
#if SOME_MACRO
return float4(1,1,1,1);
#else
return float4(0,0,0,1);
#endif
以上代碼結果返回黑色
// #if 的使用 //
#define SOME_MACRO
#if SOME_MACRO
return float4(1,1,1,1);
#else
return float4(0,0,0,1);
#endif
以上代碼會報錯: invalid or unsupported integer constant expression
詳細信息可以參考 #if文檔
#ifdef
#ifdef 用于判斷一個常量或者宏是否被定義
格式是: #ifdef identifier
identifier是一個宏或者常量,可以通過#define指令來定義,如果identifier被定義過,則#ifdef 語句塊內的代碼會被編譯,否則不會被編譯。
#ifdef 只是判斷一個常量或者宏是否被定義,不可以用于表達式判斷,但是#if可以,例如:
#define CONST_VALUE 3
#if CONST_VALUE > 1
return float4(1,1,1,1);
#else
return float4(0,0,0,1);
#endif
#ifdef CONST_VALUE > 1 // 這樣寫會報錯 //
hlsl文檔上的說法是#ifdef這種寫法只是為了兼容以往版本,建議使用defined來判斷,即使用 #if defined(MACRO) 這種形式,接下來就會說到。
These directives are provided only for compatibility with previous versions of the language. The use of the defined operator with the #if directive is preferred.
詳細信息可以參考 #ifdef文檔
#if defined
這是文檔里更推薦的一種寫法
#if defined 和 #ifdef 都可以用來判斷一個宏是否被定義,#if !defined 等同于 #ifndef。
#define SOME_MACRO
// #if defined 的使用 //
#if defined(SOME_MACRO)
// do something //
#elif defined(OTHER_MACRO)
// do something else //
#endif
// #ifdef 的使用 //
#ifdef SOME_MACRO
// do something //
#endif
#ifdef OTHER_MACRO
// do something else //
#endif
總結
- 如果是根據一個宏是否被定義來決定一段代碼要不要執行,建議使用 #if defined(MACRO) 方式
- 如果是根據一個表達式的值是否為0來決定一段代碼要不要執行,建議使用 #if condition 方式
- #ifdef 用于判斷宏或者常量是否被定義,不用于判斷表達式,#if 可以用于判斷表達式
參考鏈接:
https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-appendix-preprocessor
https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-appendix-pre-define
https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-appendix-pre-if
https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-appendix-pre-ifdef
https://stackoverflow.com/questions/3802988/difference-between-preprocessor-directives-if-and-ifdef
測試工程地址:
https://github.com/JasonTheCoderMichael/Preprocessor-Directives