我试图理解这里的代码。
但是我在理解这里的定义时遇到了问题:
...
#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_BOOL
,
GTYPE_I8
等是在描述基本类型的 enum
中定义的值。
关于c - 这段代码中定义 "#define MK_CMD(x) void cmd_ ## x (arg_t*)"是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48857596/