GCC 預處理的巨集 (predefined macros)
關於巨集(macro) 在很早很早就知道, 但是隻限於能看懂,在自己程式設計中從未使用。但是,在Powe8上將GATK HaplotypeCaller的GPU實現和SIMD實行並行處理的時候,遇到了很大的問題。找不到 g_compute_full_float() 函式的定義,因此開始覺得要認真整理一下思路。
1. 首先,編譯器已經預定義了一些巨集。
2.編譯器自己預定一定的巨集,在不同的引數下顯示結構不同。 power8 上 有-mcpu=power8 的結果不同。
如何找到編譯器預定義的巨集 (predefined macros)
沒有專門的標頭檔案定義(或許在編譯器的源程式中,但是我們一般不會去查原始檔)。常用命令找出所有的predefined macros.
1. 在使用gcc/g++編譯器時,可以通過以下命令打印出編譯器預編譯的巨集。
gcc -dM -E - < /dev/null
or:
You can also do "cpp -dM </dev/null", which invokes preprocessor directly. 【cpp是GPP的Preprocessor】
(1) gcc exists on systems where /dev/null means nothing
(2) By default, gcc -dM
will read its input file from standard input and write to standard output. Since you're not trying to preprocess any input, you can pass it the empty input using /dev/nul
(3) Note that some preprocessor defines are dependent on command line options - you can test these by adding the relevant options to the above command line. For example, "gcc -dM -E - < /dev/null" and "gcc -dM -E -mcpu=power8 - < /dev/null" are not the same.
下面完全不知道在說啥,應該是和C++11有關的,額。
*******************************************************************************************************
The simple approach (gcc -dM -E - < /dev/null
) works fine for gcc but fails for g++. Recently I required a test for a C++11/C++14 feature. Recommendations for their corresponding macro names are published at https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations. But:
g++ -dM -E - < /dev/null | fgrep __cpp_alias_templates
always fails, because it silently invokes the C-drivers (as if invoked by gcc
). You can see this by comparing its output against that of gcc or by adding a g++-specific command line option like (-std=c++11) which emits the error message cc1: warning: command line option ‘-std=c++11’ is valid for C++/ObjC++ but not for C
.
Because (the non C++) gcc will never support "Templates Aliases" (see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf) you must add the -x c++
option to force the invocation of the C++ compiler (Credits for using the -x c++
options instead of an empty dummy file go to yuyichao, see below):
g++ -dM -E -x c++ /dev/null | fgrep __cpp_alias_templates
There will be no output because g++ (revision 4.9.1, defaults to -std=gnu++98) does not enable C++11-features by default. To do so, use
g++ -dM -E -x c++ -std=c++11 /dev/null | fgrep __cpp_alias_templates
which finally yields
#define __cpp_alias_templates 200704
noting that g++ 4.9.1 does support "Templates Aliases" when invoked with -std=c++11
.
**************************************************************************************************
2. 在VS IDE中,可以參看http://msdn.microsoft.com/zh-cn/library/b0084kay。
使用者自定義巨集:
(1)在程式碼中通過#define自定義巨集外,
(2)還可以在編譯原始檔的時候通過配置編譯器實現.
1. 在使用gcc/g++編譯器時,通過新增 -D 選項,新增使用者自定義的巨集。
例如: g++ -o test -D USERM test.cpp
這樣就在預處理test.cpp之前,添加了 USERM這個巨集定義。
2. 在VS IDE中,可以點選專案的properties頁面,選擇C++頁選項,點選preprocessor,在右邊的preProcessor Definitions中新增使用者自己的巨集,
同樣,也會在project預處理之前新增自定義的巨集。
ANSI C標準中有幾個標準預定義巨集(也是常用的):
- __LINE__:在原始碼中插入當前原始碼行號;
- __FILE__:在原始檔中插入當前原始檔名;
- __DATE__:在原始檔中插入當前的編譯日期
- __TIME__:在原始檔中插入當前編譯時間;
- __STDC__:當要求程式嚴格遵循ANSI C標準時該標識被賦值為1;
- __cplusplus:當編寫C++程式時該識別符號被定義。
行連線符\
當巨集定義一行程式碼無法寫完時, 可以使用反斜槓\續行.即在每行巨集後面新增\.
前處理器會把以\結束的巨集指令的末尾的\和換行符刪除掉,把若干巨集指令行合併成一行.
gcc生成預處理後文件
gcc/g++可以通過命令引數輸出預處理後的檔案.
對於C預處理後的檔案字尾是.i, c++處理後文件字尾是.ii
命令如下:
1 2 3 |
g++ -E main.cpp //對main.cpp預處理後結果輸出到標準stdout g++ -c -save-temps main.cpp //生成預處理後main.ii檔案 gcc -c -save-temps main.c //生成預處理後main.i檔案 |
生成的.i/.ii檔案會原來的cpp檔案大很多, 因為把標頭檔案的資訊都完整包含進來了.
並且原始碼中的巨集定義都被處理了, 在預處理後文件中都不見了.
避免標頭檔案多次被引用
C/C++中的巨集最常用的就是通過#ifdef, #endif來避免標頭檔案多次被引用(redefinition).
C++ 03標準中有一條ODR(One Definition Rule)準則, 裡面提到:
In any translation unit, a template, type, function, or object can have no more than one definition. Some of these can have any number of declarations. A definition provides an instance.
就是說C++中的模版,型別, 函式, 物件可以有多個宣告, 但最多有1個定義.
如果違反ODR原則, 編譯器會提示redefinition錯誤.
如果防止標頭檔案中的定義被多次包含呢? 答案就是巨集定義.
對每個標頭檔案都定義一個獨特唯一標識的巨集,通過判斷該巨集是否被定義, 而決定是否包含該標頭檔案.
標準模版如下:
1 2 3 4 5 6 7 |
//標頭檔案name.h #ifndef NAME_H #def NAME_H ... //name.h檔案定義的記憶體 #endif |
一般來說, 對於標頭檔案定義的巨集, 命名規則通常為NAME_H, 其中NAME為標頭檔案名字的大寫.
高山仰止,景行行止。雖不能至,然心嚮往之。