C1X系列:type-generic macros
承蒙轉載,請保持本文的完整性,請匆用於商業用途。
type-generic macros在新的C1X草案又稱爲Generic selection,根據它的提案和最新C1X草案,可將type-generic macros翻譯成泛型宏或者通用類型宏。 type-generic macros是一種編譯期技術,它允許開發人員根據宏的某個參數的類型來確定生成的內容。這與C++中的函數重載有點類似,但又有不同。
type-generic macros並不是 C1X提出的概念,早在C99時就有了,只不過當時沒有對它進行標準化。 C99中引用了頭文件<tgmath.h>,給開發人員提供大量初等數學數函接口。
在C99中,程序員可以以不同的類型來調用 sin函數, 比如:
sin(1), 實際上調用sin(1.0)
sin(1.0F), 實際上調用sinf(1.0F)
sin(1.0L),實際上調用sinl(1.0L)
實際上,sin這個接口是一個宏,它會用戶傳遞的實際類型來決定最終調用的函數。 sin就是一個type-generic macro。
1. type-generic macros的技術現狀
爲了實現C99中tgmath.h中的接口,各個編譯器八仙過海,各顯神通。
Edison Design Group(EDG)爲實現tgmath.h提出了一種編譯魔法,讓開發人員(庫的開發者)使用__generic可輕鬆實現tgmath.h的接口。如上述的sin接口可定義如下:
#define sin(x) __generic(x,,, sin, sinf, sinl, csin, csinf, csinl)(x)
__generic就是EDG的把戲,它並不生成生代碼,只在編譯期,根據x的類型float, double, long double, float complex, double complex或long double complex來選擇後面對應的6個函數。
當然EDG的__generic用來實現C1X的type-generic macros是不行的,它只能識別浮點數和複數一共6種類型,並且__generic後的六種函數必須按這種順序來排列, 但對C99標準中的generic function,這種技術已足夠了。
gnu GCC算得上是一個體貼入微的編譯器,它在不違背標準和C語義的情況下,提供很多編譯時的內置小工具,讓你編寫功能強太,可以控制更底層的代碼。當然,這些feature只能在gcc上使用。
C99中的sin在gcc可以蟬聯 __builtin_choose_expr,__builtin_types_compatible_p和typeof來實現。
- #define sin(x) /
- __builtin_choose_expr ( /
- __builtin_types_compatible_p (typeof (x), long double), /
- sinl(x), /
- __builtin_choose_expr ( /
- __builtin_types_compatible_p (typeof (x), double), /
- sinf(x), /
- sin(x)))
等修飾符後的類型), 而__builtin_choose_expr 是編譯期的三元組運算符,如果條件爲true,則運算第一個表達式,否則運算第二個 表達式。gcc編譯器的tgmath.h實現,並沒有使用上述的__builtin_技術,而是使用一些sizeof, typeof和其它__builtin_,但可讀性非常差。
C1X之type-generic macros
目前type-generic macros有兩個提案,分別是N1340 Extensible Generic Math Functions 和N1404 General support for type-generic macros . N1340從C99的tgmath實現談起,提出將EDG的實現方案進行標準化,以解開發人員編寫可移植的泛型宏之苦。N1404在N1340的基礎上,對type-generic macros的語法形式方面進行改進,現已成形於最新的C1X草案中。
C1X中引入了新關鍵字_Generic來實現type-generic macros, 這就一個語法糖魔術,它根據第一個參數的類型,和後面的類型-表達式關聯來實現編譯期的替換。它的語法格式規定如下:
- generic-selection:
- _Generic ( assignment-expression , generic-association-list )
- generic-association-list:
- generic-association
- generic-association-list , generic-association
- generic-association:
- type-name : assignment-expression
- default : assignment-expression
利用_Generic,C99的sin 可定義如下:
- #define sin(x) /
- _Generic(x, /
- long double: sinl, /
- double: sinf, /
- defualt: sin /
- )(x)
_Generic對第一個參數進行類型判斷,然後根據從第二參數開始的類型-表達式關聯表來進行編譯期替換(或生成)。如果x爲long double類型,那麼_Generic(x, …)的結果爲sinl,如果x爲double類型,那麼_Generic(x,…)的結果爲sinf,否則結果爲sin。可見_Generic實現就是一個generic selection。
有了_Generic,原本只有編譯器才能玩的語法把戲,我們一樣可以玩。例如編一個sum的數學接口,如下:
- int sumi(int *arr, int cnt)
- {
- int sum = 0;
- int i;
- for(i = 0; i < cnt; ++i) {
- sum += arr[i];
- }
- return sum;
- }
- double sumf(double *arr, int cnt)
- {
- double sum = 0.0;
- int i;
- for(i = 0; i < cnt; ++i) {
- sum += arr[i]
- }
- return sum;
- }
- #define sum(_arr, _cnt) /
- _Generic(_arr[0], /
- int: sumi, /
- defualt: sumf /
- )(_arr, _cnt)
如果_Generic中的參數可以支持更復雜的表達式,那就可以用來實現C++中的部分元編程,可惜它的參數表達式有限制。 不管怎麼說_Generice是用來實現泛型宏的,而不是給開發人員任何變換的法戲,因此它的價值在於方便編寫可移植的泛型宏。
from: http://blog.csdn.net/linyt/article/details/5966485