Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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.

Furthermore, the clauses are user-definable.

https://www.kylheku.com/cgit/cppawk/about/

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.


Other than some more clever hacks, templates alongside constexpr already cover a lot of possibilities.


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.


I don't need to imagine when we have Circle, and experimental implementations of C++ reflection.

That is what is missing piece of the puzzle.


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.


> It's a huge reminder C++ is missing a proper, hygienic, macro system.

Templates, man.


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

A good perspective.


Bjarne was right, although C++ has been slow to adopt replacements for the preprocessor (such as modules, and conditional compilation).


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.

Example code

#include <type_traits>

#include <utility>

#include <assert.h>

#include <stddef.h>


Why use template metaprogramming? You can use compiler extensions for that since the code will be valid either way


C++ has the Maximum Pain Rule.

Templates it must be.


There is a branch of clang with support for "-Wlifetime" which might be a simpler alternative.


It is not like I didn't try...


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:

    #include <boost/spirit.hpp>
    using namespace boost;
    int main() {
      spirit::rule<> group, fact, term, expr;
      group   = '(' >> expr >> ')';
      fact    = spirit::int_p   | group;
      term    = fact >> *(('*' >> fact) | ('/' >> fact));
      expr    = term >> *(('+' >> term) | ('-' >> term));
      assert( spirit::parse("2*(3+4)", expr).full );
      assert( ! spirit::parse("2*(3+4", expr).full );
    }
https://studylib.net/doc/10029968/text-processing-with-boost... slide 40

What's C++ and what's regex?


Aren't these just normal parser combinators?


A >> means right shift, except when it doesn't in the 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.)


Defining your own operators means combining the lexer with the semantic analysis, which comes with all sorts of complexities.


Actually, no. At least the way it's handled in Haskell and OCaml avoids this problem.

(Btw, parsing C++ is already Turing complete.)


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 :(


The problem with no info on parameters and horrible template errors are related:

The root cause is that template expansion is duck-typed. 'Concepts' are supposed to fix that, I heard.


Can it be used to implement automatic serialization on simple struct / class types?


If you only use std::tuple, then yes xD


There is a nasty trick which uses structured binding to convert an arbitrary aggregate to a tuple.

That still doesn't help if you want the fields names.


You have any link?



Thanks. This may be useful for me someday.


No, because C++ templates are basically a Lisp, and only things that are list-like can be processed by templates. (Like tuples, for example.)


That's a bit confusing. Lisp has all kinds of datatypes.

But you are right that C++ templates are a bad functional programming language with duck typing.


But there are automated ways to to convert aggregates to tuples.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: