对于这个宏:
#define ncmp(x, y) ((x) > (y)) - ((x) < (y))
主要问题是:
它需要在展开式中附加一组括号来形成主表达式。展开式中没有足够的括号将其变成主表达式。它应该是:
#define ncmp(x, y) (((x) > (y)) - ((x) < (y)))
它评估(x)
和(y)
两次,如果评估有副作用,这可能是一个问题。
为了避免多重评估的问题,宏扩展可以使用一个通用的选择表达式来为每个被比较的类型调用不同的函数。
注 1:2011 版 C 标准 (C11) 中增加了泛型选择。)
这是一个使用通用选择的示例宏。它可能需要扩展以支持其他类型:
#define ncmp(x, y) _Generic((x) < (y), \
int: ncmp_si, \
unsigned: ncmp_ui, \
long: ncmp_sli, \
unsigned long: ncmp_uli, \
long long: ncmp_slli, \
unsigned long long: ncmp_ulli, \
float: ncmp_f, \
double: ncmp_d, \
long double: ncmp_ld \
)((x), (y))
注 2:(x) < (y)
不计算泛型选择 ( ) 的控制表达式,但其类型用于选择相应的泛型关联表达式(如果有)。
注3:<
控制表达式中的选择无关紧要,但它至少检查(x)
并(y)
具有有序关系。对于算术操作数,控制表达式的类型是通常算术转换的结果。
注 4:由于<
在控制表达式中对 的操作数进行了通常的算术转换,因此不需要为 的等级以下的整数类型添加案例int
。
注 5:可以添加default:
通用关联。例如,可以定义回退到使用不太安全的多重评估方法,如下所示:
#define ncmp(x, y) _Generic((x) < (y), \
int: ncmp_si((x), (y)), \
unsigned: ncmp_ui((x), (y)), \
long: ncmp_sli((x), (y)), \
unsigned long: ncmp_uli((x), (y)), \
long long: ncmp_slli((x), (y)), \
unsigned long long: ncmp_ulli((x), (y)), \
float: ncmp_f((x), (y)), \
double: ncmp_d((x), (y)), \
long double: ncmp_ld((x), (y)), \
default: ((x) > (y)) - ((x) < (y)) \
)
但我选择让程序员来添加缺失的案例。
有必要定义上面每个通用关联所使用的函数。为了节省一点打字,可以定义一个辅助宏来定义它们:
#define MK_NCMP_(suf, T) \
static inline int ncmp_##suf(T x, T y) { return (x > y) - (x < y); }
MK_NCMP_(si, int)
MK_NCMP_(ui, unsigned)
MK_NCMP_(sli, long)
MK_NCMP_(uli, unsigned long)
MK_NCMP_(slli, long long)
MK_NCMP_(ulli, unsigned long long)
MK_NCMP_(f, float)
MK_NCMP_(d, double)
MK_NCMP_(ld, long double)