2

I'm trying to understand the code here.

Simple Interpreter

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?

Zeru Zhang
  • 79
  • 1
  • 7
  • What part exactly you don't understand? Is it the `##`? – HolyBlackCat Feb 18 '18 at 23:14
  • You don't show MK_CMD being used; why are you asking about it? – Jonathan Leffler Feb 18 '18 at 23:24
  • You may (or may not) find [How to concatenate twice with the C preprocessor and expand a macro as in “arg ## _ ## MACRO”?](https://stackoverflow.com/questions/1489932/) — though it is arguably dealing with one stage more complexity as it expands one of the arguments if it is a macro. – Jonathan Leffler Feb 18 '18 at 23:53
  • @JonathanLeffler Yes that's what I'm looking for, I tried to copy the entire code here but I could only post code line by line, sorry for that. – Zeru Zhang Feb 19 '18 at 00:19
  • @HolyBlackCat Yes! Problem solved, thank you tho. – Zeru Zhang Feb 19 '18 at 00:19

2 Answers2

4

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.

Pablo
  • 13,271
  • 4
  • 39
  • 59
2

## 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.

antiduh
  • 11,853
  • 4
  • 43
  • 66