Introduction
It’s been a while since I first started updating cdecl. From my previous article:
Cdecl (see-deh-kull) is a program for composing and deciphering C (or C++) type declarations or casts, aka “gibberish.”
In the nearly seven years (!) since that article, I have, in fact, kept hacking on cdecl. In that time, I’ve of course added support for many more C & C++ language features (all the way through C23 and C++23). Additionally, I’ve added support for:
- Configuration files.
- Context- and language-sensitive autocompletion.
- “Did you mean ...?” suggestions in error messages.
- “East const.”
- Embedded C.
- Microsoft calling conventions.
- Scoped names.
- Unified Parallel C.
Recently, I was doing some research on C preprocessor macros and came across this answer on how to write a set of macros to allow one to implement functions with default arguments in C (yes, C). Saying the macros are complicated is an understatement. As I noted:
Complicated macros are hard to debug because you only ever see the end result of the expansion. There’s no way to see how expansion progresses step-by-step.
So I got the “itch” to write a program to do just that: given a set of macros, expand them step-by-step as an aid to understanding how they work.
While I could have written an entire new program, I decided to add the feature to cdecl. Although macro expansion doesn’t have anything to do with explaining either C or C++ declarations, cdecl already had code for lexing, parsing, hash tables, printing errors and warnings with source location information (and in color), and lots of other useful code that otherwise would have to have been duplicated.
That aside, adding this feature turned out to be quite the undertaking since it’s effectively implementing full C preprocessor macro expansion with all its weird rules.
Expanding Macros
But now that it’s done, you can do things like this:
cdecl> #define PASTE_HELPER(A,B) A ## B
cdecl> #define PASTE(A,B) PASTE_HELPER(A,B)
cdecl> expand PASTE(var_, __LINE__)
PASTE(var_, __LINE__) => PASTE_HELPER(A,B)
| A => var_
| B => __LINE__
| | __LINE__ => 42
| B => 42
PASTE(var_, 42) => PASTE_HELPER(var_,42)
| PASTE_HELPER(var_, 42) => A ## B
| PASTE_HELPER(var_, 42) => var_ ## 42
| PASTE_HELPER(var_, 42) => var_42
PASTE(var_, 42) => var_42
If I do say so myself, that’s pretty nifty. (The actual output includes color, in particular text that’s substituted is colored differently so you can easily distinguish it. Yes, the colors are fully customizable.)
Warnings
As shown, the preprocessor operators of #
and ##
have a pitfall that often ensnares new users. A naive implementation of PASTE
is:
#define PASTE(A,B) A ## B
When you expand it, cdecl will warn you (something a real C preprocessor won’t do):
cdecl> expand PASTE(var_, __LINE__)
PASTE(var_, __LINE__) => A ## B
PASTE(var_, __LINE__) => var_ ## __LINE__
^
34: warning: "##" doesn't expand macro arguments; "__LINE__" will not expand
PASTE(var_, __LINE__) => var___LINE__
Pretty nifty, again.
Conclusion
Cdecl is still a fun project to hack on. If history is any guide, I’ll probably keep hacking on cdecl.