我使用C11 _Generic关键字编写了一个泛型和宏,但是我在扩展其他泛型函数定义中的宏时遇到了问题。
以下是包含所有辅助宏的sum宏:
// Helper macros
#define _num_map($, _) $(char)_ $(short)_ $(int)_ $(long)_ $(float)_ $(double)
#define _comma ,
#define _gencase(_T, _F) _T: _F(_T)
#define _alen(_xs) sizeof(_xs)/sizeof(_xs[0])
#define _generic(_map, _M, _gen, ...) _Generic((_gen), _map(_M, _comma))(__VA_ARGS__)
// Define sum functions
#define _sum_func(_T) __sum_generic_##_T
#define _def_sum(_T) \
_T _sum_func(_T)(_T *xs, size_t len) \
{ \
_T n = (_T) 0; \
for (size_t i = 0; i < len; i++) \
n += xs[i]; \
return n; \
}
_num_map(_def_sum,)
#undef _def_sum
// Define sum$ and suma$ generic macros:
#define _gen_sum(_T) _gencase(_T, _sum_func)
#define sum$(_xs, _len) _generic(_num_map, _gen_sum, (_xs)[0], _xs, _len)
#define suma$(_xs) sum$(_xs, _alen(_xs))
使用 _num_map
是为了避免必须复制函数定义宏和_Generic表达式中的类型。它本质上只是一个列表,内置宏映射,将_
字符视为,
,而$
字符是映射宏。
$
和sum$
末尾的suma$
符号只是个人风格指南,表示“通用宏,如果您使用,请注意可能的奇怪的编译器错误/警告错了。“
这很好用,可以像这样使用:
int main(void)
{
int nums[] = {1, 2, 3, 4};
double dubs[] = {1.1, 2.2, 3.3, 4.4};
double dubs0[] = {};
printf("Sum: %d\n", suma$(nums));
printf("DSum: %lf\n", suma$(dubs));
printf("Zero DSum: %lf\n", suma$(dubs0));
return 0;
}
当我尝试做这样的事情时,问题出现了:
// Define avg functions
#define _avg_func(_T) __avg_generic_##_T
#define _def_avg(_T) \
double _avg_func(_T)(_T *xs, size_t len) \
{ \
return ((double) sum$(xs, len)) / len; \
}
_num_map(_def_avg,)
#undef _def_avg
// Define avg$ and avga$ generic macros:
#define _gen_avg(_T) _gencase(_T, _avg_func)
#define avg$(_xs, _len) _generic(_num_map, _gen_avg, (_xs)[0], _xs, _len)
#define avga$(_xs) avg$(_xs, _alen(_xs))
_num_map(_def_avg,)
无法正常展开,查看gcc -E sum-generic.c
的输出我可以看到_num_map
在sum$
内扩展_def_avg
时被视为函数调用}}。因此,gcc
出于某种原因,在_num_map
已经展开_num_map
时,不希望继续展开_num_map
。
解决问题涉及重复// Define avg functions
#define _avg_map($, _) $(char)_ $(short)_ $(int)_ $(long)_ $(float)_ $(double)
#define _avg_func(_T) __avg_generic_##_T
#define _def_avg(_T) \
double _avg_func(_T)(_T *xs, size_t len) \
{ \
return ((double) sum$(xs, len)) / len; \
}
_avg_map(_def_avg,)
#undef _def_avg
// Define avg$ and avga$ generic macros:
#define _gen_avg(_T) _gencase(_T, _avg_func)
#define avg$(_xs, _len) _generic(_avg_map, _gen_avg, (_xs)[0], _xs, _len)
#define avga$(_xs) avg$(_xs, _alen(_xs))
:
avg$
我不喜欢这个解决方案有三个原因:
sum$
应继承sum$
中可接受的输入类型列表,因为它只支持_num_map
支持的类型_integer_map
和_floating_map
这样的事情时,像#define _avg_map _num_map
这样的宏在任何地方都可以使用。类型,可让您轻松地对类型进行分类。现在你必须复制所有使它对全局分类不太有用的东西。使用_num_map
作弊不起作用,嵌套gcc
也不行。
所以我的问题是,有没有办法强制_num_map
编译器继续扩展 _num_map(_def_avg)
内的_num_map
?或者我只需复制set
?
答案 0 :(得分:0)
不,C宏永远不会递归。 6.10.3.4 p 2读取:
如果在此扫描期间找到要替换的宏的名称 替换列表(不包括源文件的其余部分 预处理令牌),它没有被替换。此外,如果有任何嵌套 替换遇到被替换的宏的名称,它不是 更换。这些未替换的宏名称预处理标记是否定的 更长的可用于进一步更换,即使它们以后 (重新)在宏名称预处理令牌的上下文中进行检查 否则就会被取代。