1. 程式人生 > >GCC 預處理的巨集 (predefined macros)

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為標頭檔案名字的大寫.

 

高山仰止,景行行止。雖不能至,然心嚮往之。