c - 这段代码中定义 "#define MK_CMD(x) void cmd_ ## x (arg_t*)"是什么意思?

标签 c

我试图理解这里的代码。

Simple Interpreter

但是我在理解这里的定义时遇到了问题:

...
#define MK_CMD(x) void cmd_ ## x (arg_t*)
...
#define CMD(func, params, help) {#func, cmd_ ## func, params, help}
...

它是如何工作的?

最佳答案

当你“懒惰”时,你会使用这种宏。有时你有一堆 几乎相同的功能,它们之间的差异很小。反而 一遍又一遍地编写相同的代码,你可以使用宏来拯救你 击键。如果你在一个中发现错误,其他人可能必须修复 又是同一个地方。拥有这样的宏可以解决问题,因为如果你 修复宏中的错误,同时修复所有功能。

宏中的##是一个连接,它允许在扩展时合并标记 宏。一个有用的地方是:

#define MK_CMD(x) void cmd_ ## x (arg_t*)
//Functions definitions
MK_CMD(prompt);
MK_CMD(load);
MK_CMD(disp);
MK_CMD(add);
MK_CMD(mul);
MK_CMD(sqrt);
MK_CMD(exit);
MK_CMD(help);

这将扩展到

void cmd_prompt(arg_t*);
void cmd_load(arg_t*);
void cmd_disp(arg_t*);
...

这是为编译器声明函数,这样它就知道有一个 名为 cmd_prompt 的函数将指向 argt_t 的指针作为参数。 有一个名为 cmd_load 的函数......

假设您后来意识到 cmd_* 函数需要第二个参数, 一个 int,那么你不必手动更改所有函数原型(prototype),你 只需将宏更改为

#define MK_CMD(x) void cmd_ ## x (arg_t*,int)

和所有其他函数都将具有该参数。看,“懒惰”的功能 程序员。

另外一个宏也属于这一类,这次是创建一个 用花括号初始化数组(这有一个我不能 现在记住),比如 int arr[3] = {1, 2, 3};

再一次,“懒惰”的程序员可能不想到处使用花括号 位置并增加可读性,所以它确实如此:

#define CMD(func, params, help) {#func, cmd_ ## func, params, help}
#define CMDS 8
cmd_t dsp_table[CMDS] ={
CMD(prompt,"s","Select the prompt for input"),
CMD(load,"cf","Load into register float"),
CMD(disp,"c","Display register"),
CMD(add,"ff","Add two numbers"),
CMD(mul,"ff","Multiply two numbers"),
CMD(sqrt,"f","Take the square root of number"),
CMD(exit,"","Exits the interpreter"),
CMD(help,"","Display this help")};

扩展为:

cmd_t dsp_table[8] = {
    {"prompt", cmd_prompt, "s", "Select the prompt for input"},
    {"load", cmd_load, "cf", "Load into register float"},
    ...
};

我在引号中使用了惰性,因为我并不一定要把它当作消极的东西。 如果使用得当,宏的这个功能非常有用,可以节省您的时间 很多时间。我过去曾将其用于封装读取和设置的库 通过类似 union 的东西来评估值(value),但更复杂。代码如下所示:

#define sensor_set_value_typed(gtype, type_short_name, c_type)\
int sensor_set_value_ ## type_short_name(sensor *sens, c_type val)\
{\
    gtype t_val;\
    gtype_id type_id;\
    if(sens == NULL)\
        return 0;\
    ...\
    gtype_init(&t_val);\
    gtype_set_type(&t_val, gtype);\
    gtype_set_value(&t_val, &val);\
    return complicated_api_set_value(sens, &t_val);\
}

我删除了很多部分代码并重命名了一些变量和函数,因为这段代码没有开源,我只是想说明一下 宏背后的想法,而不揭示幕后发生的一切。该算法是 99% 的代码相同,只有 gtype 信息不同, 这些函数可以用作更笨重的封装类型的包装器 图书馆。但是为了在没有宏的情况下这样做,我将不得不进行大量的复制和粘贴并为所有这些更改一行 功能。如果我在其中一个包装器上发现一个错误,我必须修复它 所有包装器上的同一位置出现错误。使用宏我可以这样做:

sensor_set_value_typed(GTYPE_BOOL, bool,  bool);
sensor_set_value_typed(GTYPE_I8, i8,  int8_t);
sensor_set_value_typed(GTYPE_U8, u8, uint8_t);
sensor_set_value_typed(GTYPE_I16, i16,  int16_t);
sensor_set_value_typed(GTYPE_U16, u16, uint16_t);
sensor_set_value_typed(GTYPE_I32, i32,  int32_t);
sensor_set_value_typed(GTYPE_U32, u32, uint32_t);
sensor_set_value_typed(GTYPE_I64, i64,  int64_t);
sensor_set_value_typed(GTYPE_U64, u64, uint64_t);
sensor_set_value_typed(GTYPE_FLOAT, float,  float);
sensor_set_value_typed(GTYPE_LONG, long, long);
sensor_set_value_typed(GTYPE_DOUBLE, double,  double);

现在我在 12 行中有 12 个基本库的包装器。现在用户可以使用 包装器

int32_t val = get_value_from_real_sensor();
sensor_set_value_i32(sensor, val);

而不是使用复杂的底层库。 GTYPE_BOOLGTYPE_I8 等是在描述基本类型的 enum 中定义的值。

关于c - 这段代码中定义 "#define MK_CMD(x) void cmd_ ## x (arg_t*)"是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48857596/

相关文章:

c - C语言中如何复制字符串

c - 将一个数组的所有元素存储到另一个数组以便处理

c - 优化代码,使其每次经过 for 循环时不再从 1 开始

C - 如何将以空格分隔的数字文本文件列表读入列表

c - 使用c使用指针反转字符串?

c - 如何访问 lua_pcall 调用的 C 函数的返回值?

objective-c - 从函数内的循环返回数字列表

ios - 如何声明枚举从一个对象传递到另一个对象

c - 如何打印完整的 32 位值?

c - 为什么来自 K&R 的某些代码在代码 :Blocks? 中不起作用