I'm trying to understand the code here.
But I'm having problem understand the define here:
...
#define MK_CMD(x) void cmd_ ## x (arg_t*)
...
#define CMD(func, params, help) {#func, cmd_ ## func, params, help}
...
How does it work?
I'm trying to understand the code here.
But I'm having problem understand the define here:
...
#define MK_CMD(x) void cmd_ ## x (arg_t*)
...
#define CMD(func, params, help) {#func, cmd_ ## func, params, help}
...
How does it work?
You use this kind of macros when you are "lazy". Sometimes you have a bunch of functions that are almost identical and they differ only very slightly. Instead of writing the same code over and over, you can use a macro to save you keystrokes. And if you find a bug in one, the others might have to fixed at the same place again. Having a macro like this solves the problem, because if you fix the bug in the macro, you fix it for all functions at the same time.
The ##
in a macro is a concatenation, it allows to merge tokens when expanding
the macro. A useful place for this is this:
#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);
This will expand to
void cmd_prompt(arg_t*);
void cmd_load(arg_t*);
void cmd_disp(arg_t*);
...
This is declaring functions for the compiler, so that it knows that there is a
function called cmd_prompt
that takes a pointer to argt_t
as an argument.
There is a function called cmd_load
that ....
Let's say you later realized that the cmd_*
functions need a second argument,
an int
, then you don't have to manually change all function prototypes, you
only have to change the macro to
#define MK_CMD(x) void cmd_ ## x (arg_t*,int)
and all other functions will have that parameter. See, a feature for the "lazy" programmer.
The other macro falls also into this category, this time is to create a
initialization for the array with curly braces (this has an specific name I cannot
remember right now), like int arr[3] = {1, 2, 3}
;
Once again, the "lazy" programmer may not want to use the curly braces all over the place and to increase readability, so it does:
#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")};
which expands to:
cmd_t dsp_table[8] = {
{"prompt", cmd_prompt, "s", "Select the prompt for input"},
{"load", cmd_load, "cf", "Load into register float"},
...
};
I use lazy in quotes, because I don't necessarily mean that as a negative thing. This features of macros can be dead useful when used properly and can save you a lot of time. I have used that in the past for a library that encapsulates reading and setting values through something like a union, but more complex. The code looks like this:
#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);\
}
I removed many parts of the code and renamed some of the variables and functions, because this code is not open source, I just want to ilustrate
the idea behind the macros without revealing all that happens behind the scenes. The algorithm is the
same for 99% percent of the code, only the gtype
information is different and
these functions can be used as a wrapper of the more clunkly type encapsulating
library. But in order to do so without the macro, I would have to make a lot of copy&paste and change one line for all those
functions. If I found one error on one of the wrappers, I have to fix the same
error at the same place on all wrappers. With the macro I can do this:
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);
and now I have 12 wrappers of the basic library in 12 lines. Now the user can use the wrapper
int32_t val = get_value_from_real_sensor();
sensor_set_value_i32(sensor, val);
instead of using the complicated underlying library. The GTYPE_BOOL
,
GTYPE_I8
, etc are values defined in an enum
which describes the basic types.
##
is the preprocessor token pasting operator, aka macro concatenation.
It is used to join together text in the preprocessor.
For instance #define type i##nt
replaces to int
. It's a way to get the pre-processor to recognize arguments that need to be concatenated to adjacent tokens.
#define MK_CMD(x) void cmd_ ## x (arg_t*)
is used by the author to declare a function prototype.
When invoked, MK_CMD(hello)
would replace to void cmd_hello(arg_t*)
.
If instead the #define were declared as #define MK_CMD(x) void cmd_x (arg_t*)
, it would only ever emit void cmd_x (arg_t*)
instead of replacing x with the macro argument.