It's a huge reminder C++ is missing a proper, hygienic, macro system. Too many things that are a pain to do in C++ would be easy with a real macro language. I have hope we'll get it sometime this decade, seeing all the work on the language since C++11 still happening more than 10 years later.
It's worth nothing that a macro system plus basic reflection is where the real power of macros lies at.
Actually, some of the problem may be with C++ itself. When the C preprocessor is used in a more flexible, dynamic language, you can do surprising things.
In the cppawk project, which combines the preprocessor with Awk, I used the preprocessor to create an iteration syntax with a vocabulary of useful clauses that combine together for parallel or cross-product iteration.
What helps is that you don't have to deal with types and declaration syntax. So many of the ideas in cppawk will not translate back to C or C++, or not without wrecking the syntax with additional arguments and whatnot.
The man page for the <iter.h> header has a section on defining a clause; I provided an example of defining a clause that iterates on alpha-numeric string ranges like from "A00" to "Z99".
As an experienced Lisp programmer (and implementor), I had to rub my eyes several times to believe I had such a thing working, under such a universally maligned and reviled preprocessor.
You don't get the guarantees for what code is generated that you do with macros and they take a lot longer to compile. Also you can't just modify the AST of the current scope like you can with macros - pretty much every single time I have to use a macro it's because I need to generate code within the current scope. Fortunately they are usually very short.
The two for me fill different niches - for generic type-safe functions, parametrised types, etc. - templates. For text generation - macros. A hygenic macro system that lets you generate AST nodes and gives you access to type information would be absolutely divine, but it doesn't seem like we're getting it. Imagine if we had a script language that had full access to the compiler's internals.
Most of the abuses of #define I have seen, a template or constexpr takes care of. There are still some cases where it is nice. But many times you probably should just write a function/method/template out of it anyway.
In case someone reads this comment as sarcasm: I got into a convo with Stroustrup about this once, back in the 90s. I said one thing I missed was the lack of macros, and he made a glancing comment about the preprocessor which I obviously dismissed and said didn’t even count. He bitterly said, “Yeah, unfortunately when something like that pollutes an ecological niche it becomes impossible to eradicate. The best I could get away with was templates.”
I'm standing by for the announcement that some caffeine-addled Boost metaprogramming madman has implemented the Rust borrow checker as a C++ template, or at least thinks that he may have, when the compilation completes sometime in the 2030s.
It's not clear how much boost.org magic they used. Failing that, a GCC extension could be needful.
From the ref:
---
Conclusion
We attempted to represent ownership and borrowing through the C++ type system, however the language does not lend itself to this. Thus memory safety in C++ would need to be achieved through runtime checks.
Don't forget function templates! D can do them, too, but we strongly discourage their use. The trouble is that people use them to create DSLs that are indistinguishable from C++ code. For example:
Yes, obviously operations with parser combinators are different that those with numbers. (Also, I find it kind of dumb to reserve short symbols for low-level operations that are rarely used in normal programming.)
Well, the problem here is the re-use of existing operators.
(That's why it's great that Haskell and other languages in that family allow you to define your own operators, instead of eg re-using bit-shifting for IO.)
Templates are nice, but they have shortcomings a more generic macro system wouldn't. They also have the issue where the more complex is your task, the more convoluted the code has to look, compilation times also increase and parsers (ergo, IDEs too) have trouble giving meaningful info on parameters. Don't even get me started on template errors because that's an atrocity on another level :(
It's worth nothing that a macro system plus basic reflection is where the real power of macros lies at.