从数字变量值生成标记的 C 宏

C macro to produce token from numeric variable value

我如何定义一个 C 宏,它会根据传递给该函数的变量值生成一个变量名(标记)?在下面的示例中,查找 MEMBER 宏的有效版本。

例如给定下面一行的结构,我需要定义一个宏,它会根据传递给函数的变量的值生成一个变量名。

struct foo {
    uint32_t bar0_data;
    uint32_t bar0_status;

    uint32_t bar1_data;
    uint32_t bar1_status;

    ...
};

#define MEMBER(x, n, f) x -> bar ## n ## _ ## f

void write_val(struct foo *foo, int which_bar)
{
    MEMBER(foo, which_bar, data) = 1;
    MEMBER(foo, which_bar, status) = 2;
}

这适用于在这个主题上有不同变体的巨型结构,因此定义一个包含成员结构数组的新结构不是一种选择。

您一定在考虑允许您 "execute" 即时生成文本的语言(例如 JavaScript)。 C 的不同之处在于,从源文本到 运行ning 二进制文件的转换发生在以下步骤中:

  1. 运行 pre-processor - 这是程序文本 修改 宏扩展和其他规则(如 #include指令)
  2. 运行 compiler - 这会将步骤 1 的结果转换为机器代码(无论是通过使用 Assembler 语言的中间步骤还是直接)
  3. Link 带有库的结果目标文件(如有必要),解析所有外部符号并生成 "final" 二进制文件,您 运行 (或 static/dynamic 库 - 取决于你的输出)

在任何情况下 运行-time 行为发生在 way 之后 pre-processor 步骤,那又怎样您正在寻找的在 C 语言中是不可能的(缺少编写一个重新编译和重新启动自身的自修改程序)

C 不允许您以这种方式创建动态名称。

您可以使用查找来模拟它 table。

然而,您实际上只想从运行时数据中找到 foo 中的特定字段。这可以通过多种方式完成。例如 switch 语句。

switch (which_bar) {
case 0: foo->bar0_data = 1;
        foo->bar0_status = 1;
        break;
...
}

如果您更愿意使用文字 table,您可以存储字段的偏移量。

offset_t bar_data[] = {
    offsetof(struct foo, bar0_data),
    offsetof(struct foo, bar1_data),
    ...
};

offset_t bar_status[] = {
    offsetof(struct foo, bar0_status),
    ...
};

#define BAR_DATA(FOO, WHICH) \
    (*(uint32_t *)((char *)(FOO) + bar_data[WHICH]))
#define BAR_STATUS(FOO, WHICH) \
    (*(uint32_t *)((char *)(FOO) + bar_status[WHICH]))

BAR_DATA(foo, which_bar) = 1;
BAR_STATUS(foo, which_bar) = 1;

但是,您似乎最好定义一个结构来表示“bar”,并在 foo.

中定义一个数组
struct bar {
    uint32_t data;
    uint32_t status;
};

struct foo {
    struct bar bar[MAX_BARS];
};

foo->bar[which_bar].data = 1;
foo->bar[which_bar].status = 1;

FWIW,假设每个实例都出现在固定的偏移量处,我发现实现这个宏的一种方法是:

#define DELTA(m1, m2) (offsetof(foo_t, m2) - offsetof(foo_t, m1))
#define MEMBER(x, n, f)                                 \
    *(uint32_t *)(((uint8_t *)& x -> bar0_ ## f) +      \
                  (DELTA(bar0_ ## f, bar1_ ## f) * n))

它不会像最初要求的那样生成变量名称标记,但可用于访问必须生成名称的值w/o。