C1X系列:type-generic macros

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来实现。


  
  
  
  
[cpp] view plain copy print ?
  1. #define sin(x)                                                     /  
  2.         __builtin_choose_expr (                                    /  
  3.           __builtin_types_compatible_p (typeof (x), long double),  /  
  4.           sinl(x),                                                 /  
  5.            __builtin_choose_expr (                                 /  
  6.             __builtin_types_compatible_p (typeof (x), double),     /  
  7.             sinf(x),                                               /  
  8.   
  9.             sin(x)))  
__builtin_types_compatible_p (typeof (x), long double) 在编译期判断x的基础类型是否为long double(即除去const, volatile
等修饰符后的类型), 而__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, 这就一个语法糖魔术,它根据第一个参数的类型,和后面的类型-表达式关联来实现编译期的替换。它的语法格式规定如下:

[cpp]  view plain copy print ?
  1. generic-selection:  
  2.     _Generic ( assignment-expression , generic-association-list )  
  3. generic-association-list:  
  4.     generic-association  
  5.     generic-association-list , generic-association  
  6. generic-association:  
  7.     type-name : assignment-expression  
  8.     default : assignment-expression  

利用_Generic,C99的sin 可定义如下:

 

[cpp]  view plain copy print ?
  1. #define sin(x)                      /  
  2.         _Generic(x,                 /  
  3.                 long double: sinl,  /  
  4.                 double: sinf,       /  
  5.                 defualt: sin        /  
  6.                 )(x)  

 

_Generic对第一个参数进行类型判断,然后根据从第二参数开始的类型-表达式关联表来进行编译期替换(或生成)。如果x为long double类型,那么_Generic(x, …)的结果为sinl,如果x为double类型,那么_Generic(x,…)的结果为sinf,否则结果为sin。可见_Generic实现就是一个generic selection。

有了_Generic,原本只有编译器才能玩的语法把戏,我们一样可以玩。例如编一个sum的数学接口,如下:

 

[cpp]  view plain copy print ?
  1. int sumi(int *arr, int cnt)  
  2. {  
  3.     int sum = 0;  
  4.     int i;  
  5.       
  6.     for(i = 0; i < cnt; ++i) {  
  7.          sum += arr[i];  
  8.     }  
  9.     return sum;  
  10. }  
  11. double sumf(double *arr, int cnt)  
  12. {  
  13.     double sum = 0.0;  
  14.     int i;  
  15.       
  16.     for(i = 0; i < cnt; ++i) {  
  17.             sum += arr[i]  
  18.     }  
  19.     return sum;  
  20. }  
  21. #define sum(_arr, _cnt) /  
  22.        _Generic(_arr[0],    /  
  23.                 int: sumi,  /  
  24.                 defualt: sumf  /  
  25.                )(_arr, _cnt)  

 

  如果_Generic中的参数可以支持更复杂的表达式,那就可以用来实现C++中的部分元编程,可惜它的参数表达式有限制。 不管怎么说_Generice是用来实现泛型宏的,而不是给开发人员任何变换的法戏,因此它的价值在于方便编写可移植的泛型宏。


from: http://blog.csdn.net/linyt/article/details/5966485

你可能感兴趣的:(编程,C语言)