Commenting Multiline Macros: A Mistake That Cost an Afternoon and an Evening

C/C++

Posted by Bruce Lee on 2024-05-09

About Me

Welcome to my blog! This is where I collect my observations and notes on programming and technology. The main subjects range from implementation details to broader ideas about programming.

Main Topics

  • Engineering Projects: Exploring implementation details and how technical systems work.
  • C/C++: Notes on language features and programming techniques.
  • The Programmer’s Perspective: Ideas about developing a career and a way of thinking as a programmer.

For more, visit the categories page.

Contact

If you have questions or would like to discuss something, please get in touch through the About page.

Thank you for reading and for your support. I hope these notes help you on your own technical journey!


Commenting Multiline Macros: A Mistake Worth Recognizing

A Correct Example

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#define some_macro(x, y) \
((x) + \
(y) )

int main(int argc, char** argv)
{
printf("use some_macro: %d\n", some_macro(5, 1));
return 0;
}

Output:

1
use some_macro: 6

The preprocessed file contains:

1
2
3
4
5
6
# 6 "test1.c"
int main(int argc, char** argv)
{
printf("use some_macro: %d\n", ((5) + (1) ));
return 0;
}

The familiar rule is visible here: a backslash immediately followed by a newline continues the macro definition onto the next physical line.

What Happens If We Add This Comment?

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#define some_macro(x, y) \
//这个宏是做加法
((x) + \
(y) )

int main(int argc, char** argv)
{
printf("use some_macro: %d\n", some_macro(5, 1));
return 0;
}

The compiler reports:

1
2
3
4
test2.c:4:13: error: expected ‘)’ before ‘+’ token
4 | ((x) + \
| ^~
| )

Inspect the preprocessed file:

1
2
3
4
5
6
7
8
9
# 4 "test2.c"
((x) +
(y) )

int main(int argc, char** argv)
{
printf("use some_macro: %d\n", );
return 0;
}

The #define has defined an empty macro. Its expansion leaves an empty argument in the printf call, while the code that originally followed the comment remains outside the macro definition.

A line such as //这个宏是做加法—meaning “this macro performs addition”—has no trailing backslash in this example. Consequently, the macro definition ends at that physical newline. The following expression is no longer part of the replacement list:

1
2
((x) +
(y) )

The compiler cannot interpret this fragment as a valid declaration at that position, so it reports an error.

What If We Continue the Comment Line Too?

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#define some_macro(x, y) \
//这个宏是做加法 \
((x) + \
(y) )

int main(int argc, char** argv)
{
printf("use some_macro: %d\n", some_macro(5, 1));
return 0;
}

Adding a backslash after the comment looks like the obvious fix. Surely that should work? Here is the compiler’s answer:

1
2
3
test3.c:9:56: error: expected expression before ‘)’ token
9 | printf("use some_macro: %d\n", some_macro(5, 1));
|

This is a particularly difficult bug to diagnose: the error is reported where the macro is invoked, rather than where the faulty definition appears. My own experience with it is described below.

A single-line comment is still wrong here. Backslash-newline splicing first joins the physical lines into one logical line. Placing a // comment in the middle effectively produces:

1
#define some_macro(x, y) //这个宏是做加法 ((x) + (y) )

What we wanted was:

1
#define some_macro(x, y) ((x) + (y) )

In the first version, the rest of the macro’s code becomes part of the comment. The preprocessed output confirms this:

1
2
3
4
5
6
# 7 "test3.c"
int main(int argc, char** argv)
{
printf("use some_macro: %d\n", );
return 0;
}

Everything after the comment marker has disappeared with the comment.

The Correct Comment Form

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#define some_macro(x, y) \
/*这个宏是做加法*/ \
((x) + \
(y) )

int main(int argc, char** argv)
{
printf("use some_macro: %d\n", some_macro(5, 1));
return 0;
}

This version works. A block comment ends at */, so the continued macro body after it is retained. The same preprocessing order explains both the failure above and this correction.

How I Made This Mistake in a Project

The project used many macros to shorten repetitive code. After reading page after page of multiline macro definitions, I inserted a comment into this one:

1
2
3
4
5
6
7
8
9
10
#define INSTPAT(pattern, ...) do { \
uint64_t key, mask, shift; \
pattern_decode(pattern, STRLEN(pattern), &key, &mask, &shift); \
if ((((uint64_t)INSTPAT_INST(s) >> shift) & mask) == key) { \
//指令模式匹配正确时,执行该基本块 \
Assert(shift == 0, "shift don't equit 0!"); \
INSTPAT_MATCH(s, ##__VA_ARGS__); \
goto *(__instpat_end); \
} \
} while (0)

I even remembered to put a backslash after the comment. Nevertheless, the compiler’s reported location was not where I had introduced the error. It pointed to a macro invocation and produced diagnostics about other code that looked obviously correct. I spent an afternoon and an evening tracing the problem before recognizing what the single-line comment had done.


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !