宏如何根据传递给它的类型定义有效的全局名称?

时间:2013-01-03 10:45:05

标签: c generics macros c11

我认为标题是不言自明的,但这里有一个例子来说明我想要实现的目标:

#define PASTE2(_0, _1)  _0 ## _1

#define DEFINE_OPS_FOR_TYPE(TYPE)                   \
    int PASTE2(do_something_with_, TYPE)(void) {    \
        /* do_something_with_<TYPE> */              \
    }

对于charint和单一措辞类型,一切正常,但是当它出现时 来自unsigned类型或其他具有多个关键字的类型,使用令牌粘贴(a ## b)不会因空白而生成有效名称(例如:do_something_with_foo bar)。

我能想到的最简单的解决方案是更改DEFINE_OPS_FOR_TYPE宏 将有效名称作为第二个参数。例如:

#define DEFINE_OPS_FOR_TYPE(TYPE, NAME_FOR_TYPE)            \
    int PASTE2(do_something_with_, NAME_FOR_TYPE)(void) {   \
        /* do_something_with_<NAME_FOR_TYPE> */             \
    }

这可以按预期工作,但我很好奇其他可能的解决方案,即使它们过于复杂。我想过使用_Generic,但我没看到它如何帮助定义名称。

你能想到另一种解决方案吗?

2 个答案:

答案 0 :(得分:2)

在你想要做的符号的声明或定义的层面上,typedef事物与所讨论类型的唯一标识符的关系并不多。 _Generic或等效的替换为时已晚,无法对预处理器有用。

但是只有有限数量的标准类型会造成这样的问题。所以你可以很容易地提出一个类型deffing的约定。

_Generic可以帮助您在此类已定义符号的用途方面提供帮助。然后,您可以在此处执行类似

的操作
_Generic((X),
  unsigned long: do_something_with_ulong,
  unsigned char: do_something with_uchar,
  ...
)(X)

P99我遵循这个方案,你会发现它已经有很多支持宏了。

答案 1 :(得分:1)

我最终使用了一个带有空参数的宏。例如:

#define STR2(x)             # x
#define STR(x)              STR2(x)
#define PASTE3(_1,_2,_3)    _1 ## _2 ## _3
#define FOO(_1,_2,_3)       PASTE3(_1, _2, _3)

printf("%s\n", STR(FOO(int,,)));
printf("%s\n", STR(FOO(unsigned, int,)));
printf("%s\n", STR(FOO(unsigned, long, long)));

尽可能see here,输出为:

int
unsignedint
unsignedlonglong

我不记得是否根据标准很好地定义了使用空宏参数,但我可以告诉你,Clang 3.1不会对-std=c11 -pedantic发出任何警告。

如果您想尝试,可以使用以下代码:

#include <stdio.h>
#include <limits.h>

#define PASTE4(_1,_2,_3,_4) _1 ## _2 ## _3 ## _4

#define DEFINE_OPS_FOR_TYPE1(T1)        DEFINE_OPS_FOR_TYPE2(T1,)
#define DEFINE_OPS_FOR_TYPE2(T1, T2)    DEFINE_OPS_FOR_TYPE3(T1,T2,)
#define DEFINE_OPS_FOR_TYPE3(T1, T2, T3)                                    \
    int PASTE4(write_,T1,T2,T3)(FILE *file, void *data) {                   \
        T1 T2 T3 foo;                                                       \
        int written = fprintf(file, fmt_specifier(foo), *((T1 T2 T3 *)data));\
        return written > 0 ? 0 : -1;                                        \
    }

#define fmt_specifier(x)                \
    _Generic((x),                       \
        int: "%i",                      \
        unsigned int: "%u",             \
        unsigned long long: "%llu",     \
        default: NULL                   \
    )

DEFINE_OPS_FOR_TYPE1(int)
DEFINE_OPS_FOR_TYPE2(unsigned, int)
DEFINE_OPS_FOR_TYPE3(unsigned, long, long)

int main() {
    int var_int = INT_MAX;
    write_int(stdout, &var_int);
    printf("\n");
    unsigned int var_uint = UINT_MAX;
    write_unsignedint(stdout, &var_uint);
    printf("\n");
    unsigned long long var_ullong = ULLONG_MAX;
    write_unsignedlonglong(stdout, &var_ullong);
    printf("\n");
    return 0
}