通用和宏和嵌套宏扩展

时间:2018-04-01 12:39:06

标签: c generics gcc preprocessor c11

我使用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_mapsum$内扩展_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

1 个答案:

答案 0 :(得分:0)

不,C宏永远不会递归。 6.10.3.4 p 2读取:

  

如果在此扫描期间找到要替换的宏的名称   替换列表(不包括源文件的其余部分   预处理令牌),它没有被替换。此外,如果有任何嵌套   替换遇到被替换的宏的名称,它不是   更换。这些未替换的宏名称预处理标记是否定的   更长的可用于进一步更换,即使它们以后   (重新)在宏名称预处理令牌的上下文中进行检查   否则就会被取代。