从数字变量值生成标记的 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 二进制文件的转换发生在以下步骤中:
- 运行
pre-processor
- 这是程序文本 修改 宏扩展和其他规则(如 #include
指令)
- 运行
compiler
- 这会将步骤 1 的结果转换为机器代码(无论是通过使用 Assembler
语言的中间步骤还是直接)
- 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。
我如何定义一个 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 二进制文件的转换发生在以下步骤中:
- 运行
pre-processor
- 这是程序文本 修改 宏扩展和其他规则(如#include
指令) - 运行
compiler
- 这会将步骤 1 的结果转换为机器代码(无论是通过使用Assembler
语言的中间步骤还是直接) - 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。