在很多情况下,将枚举与数组元素关联起来很有用,这样枚举名称始终与数组元素保持同步。 对于这种事情,“定义宏的宏”可以很好地完成这项工作:
#define EXPAND_COLORS \
DEFINE_COLOR(COLOR_RED, 0xff0000) \
DEFINE_COLOR(COLOR_GREEN, 0x00ff00) \
DEFINE_COLOR(COLOR_BLUE, 0x0000ff)
// The following expands into an enum with a defined symbol for each color:
// typedef enum { COLOR_RED, COLOR_GREEN, COLOR_BLUE, } color_t;
//
#undef DEFINE_COLOR
#define DEFINE_COLOR(_name, _value) _name,
typedef enum { EXPAND_COLORS } color_t;
// The following expands in to an array of color values:
// int color_map[] = { 0xff0000, 0x00ff00, 0x0000ff, };
//
#undef DEFINE_COLOR
#define DEFINE_COLOR(_name, _value) _value,
int color_map[] = { EXPAND_COLORS };
// The following expands into an array of strings, one string for each color:
// const char *color_names[] = { "COLOR_RED", "COLOR_GREEN", "COLOR_BLUE", };
//
#undef DEFINE_COLOR
#define DEFINE_COLOR(_name, _value) #_name,
const char *color_names[] = { EXPAND_COLORS };
您可以看到这如何保证使用符号 COLOR_GREEN 作为索引将始终引用
0x00ff00
中的值 color_map[]
和 color_names[]
中的字符串“COLOR_GREEN”,并且您可以轻松添加新颜色:
...
DEFINE_COLOR(COLOR_AUBERGENE, 0x693b58) \
...只需在一处添加即可。
如果我想使用结构而不是将颜色表示为单个十六进制值怎么办:
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
} color_t;
我想做这样的事情:
#define EXPAND_COLORS \
DEFINE_COLOR(COLOR_RED, {.r = 0xff, .g = 0x00, .b = 0x00}) \
DEFINE_COLOR(COLOR_GREEN, {.r = 0x00, .g = 0xff, .b = 0x00}) \
DEFINE_COLOR(COLOR_BLUE, {.r = 0x00, .g = 0x00, .b = 0xff}) \
DEFINE_COLOR(COLOR_AUBERGENE, {.r = 0x69, .g = 0x3b, .b = 0x58})
// I would this to expand to:
// color_t color_map[] = { {.r = 0xff, .g = 0x00, .b = 0x00},
// {.r = 0x00, .g = 0xff, .b = 0x00},
// ...
// };
#undef DEFINE_COLOR
#define DEFINE_COLOR(_name, _value) _value,
color_t color_map[] = { EXPAND_COLORS };
上面的内容不会像写的那样工作,因为 C 预处理器将逗号视为字段分隔符:
error: macro "DEFINE_COLOR" passed 4 arguments, but takes just 2
我可以使用一些语法技巧将结构初始值设定项传递给 C 预处理器,以便它将初始值设定项解释为单个参数吗?
(我想出的最好的方法是将结构值作为单独的参数传递给宏。这可行,但我的实际用例具有带有大量预设字段的复杂嵌套结构,因此最好将嵌套结构可见。)
您可以使用
...
接受带逗号的“参数”:
#define DEFINE_COLOR(_name, ...) __VA_ARGS__,
也就是说,(IMO)让宏成为一个参数要好得多,因此您可以给它一个合理的名称,而不必 #undef 并重新定义相同的宏,因此更清楚发生了什么:
#define COLOR_LIST(M) \
M(COLOR_RED, {.r = 0xff, .g = 0x00, .b = 0x00}) \
M(COLOR_GREEN, {.r = 0x00, .g = 0xff, .b = 0x00}) \
M(COLOR_BLUE, {.r = 0x00, .g = 0x00, .b = 0xff}) \
M(COLOR_AUBERGENE, {.r = 0x69, .g = 0x3b, .b = 0x58})
#define COLOR_ENUM_VAL(_name, ...) _name,
#define COLOR_INIT_VAL(_name, ...) __VA_ARGS__,
enum colors { COLOR_LIST(COLOR_ENUM_VAL) };
color_t color_map[] = { COLOR_LIST(COLOR_INIT_VAL) };
这对你有用吗?
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
} color_t;
#define INIT_RGB(rv, gv, bv) { .r = (rv), .g = (gv), .b = (bv) }
#define EXPAND_COLORS \
DEFINE_COLOR(COLOR_RED, INIT_RGB(0xFF, 0x00, 0x00)) \
DEFINE_COLOR(COLOR_GREEN, INIT_RGB(0x00, 0xFF, 0x00)) \
DEFINE_COLOR(COLOR_BLUE, INIT_RGB(0x00, 0x00, 0xFF)) \
DEFINE_COLOR(COLOR_AUBERGINE, INIT_RGB(0x69, 0x3B, 0x58))
#undef DEFINE_COLOR
#define DEFINE_COLOR(_name, _value) _value,
color_t color_map[] = { EXPAND_COLORS };
运行
cpp xmacro71.c
,我得到输出:
# 0 "xmacro71.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "xmacro71.c"
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
} color_t;
# 19 "xmacro71.c"
color_t color_map[] = { { .r = (0xFF), .g = (0x00), .b = (0x00) }, { .r = (0x00), .g = (0xFF), .b = (0x00) }, { .r = (0x00), .g = (0x00), .b = (0xFF) }, { .r = (0x69), .g = (0x3B), .b = (0x58) }, };
重新格式化,得到:
# 0 "xmacro71.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "xmacro71.c"
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
} color_t;
# 19 "xmacro71.c"
color_t color_map[] =
{
{ .r = (0xFF), .g = (0x00), .b = (0x00) },
{ .r = (0x00), .g = (0xFF), .b = (0x00) },
{ .r = (0x00), .g = (0x00), .b = (0xFF) },
{ .r = (0x69), .g = (0x3B), .b = (0x58) },
};
我在答案的第一版中遇到的一个问题 - 不要调用宏参数
r
、g
、b
;如果你这样做,你最终会得到如下输出:
{ . 0xFF = (0xFF), . 0x00 = (0x00), . 0x00 = (0x00) },
这不是你想要的!
如果您希望保持初始化程序的显式性可见,您还可以使用:
/* SO 7121-4579 */
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
} color_t;
#define INIT_RGB(rv, gv, bv) { rv, gv, bv }
#define EXPAND_COLORS \
DEFINE_COLOR(COLOR_RED, INIT_RGB(.r = 0xFF, .g = 0x00, .b = 0x00)) \
DEFINE_COLOR(COLOR_GREEN, INIT_RGB(.r = 0x00, .g = 0xFF, .b = 0x00)) \
DEFINE_COLOR(COLOR_BLUE, INIT_RGB(.r = 0x00, .g = 0x00, .b = 0xFF)) \
DEFINE_COLOR(COLOR_AUBERGINE, INIT_RGB(.r = 0x69, .g = 0x3B, .b = 0x58))
#undef DEFINE_COLOR
#define DEFINE_COLOR(_name, _value) _value,
color_t color_map[] = { EXPAND_COLORS };
这会产生本质上相同的输出 - 区别主要在于此示例中的值周围缺少括号(缺少括号是因为它们不存在于像
.r = (0xFF)
这样的参数中)。
解决嵌套结构是可行的。 这对我也有用:
/* SO 7121-4579 */
typedef struct
{
uint8_t r;
uint8_t g;
uint8_t b;
} color_t;
#define INIT_RGB(rv, gv, bv) { rv, gv, bv }
#define EXPAND_COLORS \
DEFINE_COLOR(COLOR_RED, INIT_RGB(.r = 0xFF, .g = 0x00, .b = 0x00)) \
DEFINE_COLOR(COLOR_GREEN, INIT_RGB(.r = 0x00, .g = 0xFF, .b = 0x00)) \
DEFINE_COLOR(COLOR_BLUE, INIT_RGB(.r = 0x00, .g = 0x00, .b = 0xFF)) \
DEFINE_COLOR(COLOR_AUBERGINE, INIT_RGB(.r = 0x69, .g = 0x3B, .b = 0x58))
#undef DEFINE_COLOR
#define DEFINE_COLOR(_name, _value) _value,
color_t color_map[] = { EXPAND_COLORS };
#undef DEFINE_COLOR
#define DEFINE_COLOR(name, value) name,
enum
{
EXPAND_COLORS
MAX_COLOR_NAME
};
#undef DEFINE_COLOR
typedef struct
{
color_t tl_corner;
color_t tr_corner;
color_t bl_corner;
color_t br_corner;
} gradient_t;
#define RGB_RED INIT_RGB(.r = 0xFF, .g = 0x00, .b = 0x00)
#define RGB_GREEN INIT_RGB(.r = 0x00, .g = 0xFF, .b = 0x00)
#define RGB_BLUE INIT_RGB(.r = 0x00, .g = 0x00, .b = 0xFF)
#define RGB_AUBERGINE INIT_RGB(.r = 0x69, .g = 0x3B, .b = 0x58)
#define EXPAND_GRADIENT \
DEFINE_GRADIENT(VARIANT1, RGB_RED, RGB_GREEN, RGB_BLUE, RGB_AUBERGINE) \
DEFINE_GRADIENT(VARIANT2, RGB_GREEN, RGB_BLUE, RGB_AUBERGINE, RGB_RED) \
DEFINE_GRADIENT(VARIANT3, RGB_BLUE, RGB_AUBERGINE, RGB_RED, RGB_GREEN) \
DEFINE_GRADIENT(VARIANT4, RGB_AUBERGINE, RGB_RED, RGB_GREEN, RGB_BLUE)
#undef DEFINE_GRADIENT
#define DEFINE_GRADIENT(name, tlc, trc, blc, brc) \
{ .tl_corner = tlc, .tr_corner = trc, .bl_corner = blc, .br_corner = brc },
static gradient_t boxes[] = { EXPAND_GRADIENT };
重新格式化后的输出(添加换行符和空格,并删除一些与宏定义对应的换行符):
# 0 "xmacro97.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "xmacro97.c"
typedef struct
{
uint8_t r;
uint8_t g;
uint8_t b;
} color_t;
# 20 "xmacro97.c"
color_t color_map[] =
{
{ .r = 0xFF, .g = 0x00, .b = 0x00 },
{ .r = 0x00, .g = 0xFF, .b = 0x00 },
{ .r = 0x00, .g = 0x00, .b = 0xFF },
{ .r = 0x69, .g = 0x3B, .b = 0x58 },
};
enum
{
COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_AUBERGINE,
MAX_COLOR_NAME
};
typedef struct
{
color_t tl_corner;
color_t tr_corner;
color_t bl_corner;
color_t br_corner;
} gradient_t;
# 54 "xmacro97.c"
static gradient_t boxes[] =
{
{
.tl_corner = { .r = 0xFF, .g = 0x00, .b = 0x00 },
.tr_corner = { .r = 0x00, .g = 0xFF, .b = 0x00 },
.bl_corner = { .r = 0x00, .g = 0x00, .b = 0xFF },
.br_corner = { .r = 0x69, .g = 0x3B, .b = 0x58 }
},
{
.tl_corner = { .r = 0x00, .g = 0xFF, .b = 0x00 },
.tr_corner = { .r = 0x00, .g = 0x00, .b = 0xFF },
.bl_corner = { .r = 0x69, .g = 0x3B, .b = 0x58 },
.br_corner = { .r = 0xFF, .g = 0x00, .b = 0x00 }
},
{
.tl_corner = { .r = 0x00, .g = 0x00, .b = 0xFF },
.tr_corner = { .r = 0x69, .g = 0x3B, .b = 0x58 },
.bl_corner = { .r = 0xFF, .g = 0x00, .b = 0x00 },
.br_corner = { .r = 0x00, .g = 0xFF, .b = 0x00 }
},
{
.tl_corner = { .r = 0x69, .g = 0x3B, .b = 0x58 },
.tr_corner = { .r = 0xFF, .g = 0x00, .b = 0x00 },
.bl_corner = { .r = 0x00, .g = 0xFF, .b = 0x00 },
.br_corner = { .r = 0x00, .g = 0x00, .b = 0xFF }
},
};