Live data from Hacker News

Recursive macros in C, demystified (once the ugly crying stops)

h4x0r.org

21–30 of 90 posts

Re: Recursive macros in C, demystified (once the ugly crying stops)

#21
post #15

Earlier quoted context omitted.

100%, that's definitely easy to do once you understand the technique.

Please?

I'm on my phone, but if you start with the top 8 lines in the code box under the ascii art, you'll get an implementation of `H4X0R_MAP()`; the bottom two lines are an example, and you can just write yourself a body that produces one term. Only thing you need to know beyond that is the stringify operator.

Re: Recursive macros in C, demystified (once the ugly crying stops)

#22
post #15

Earlier quoted context omitted.

100%, that's definitely easy to do once you understand the technique.

Please?

And I should say, if you want to apply the same transformation to arguments twice, but call F() separately from G() per your starting example, you'd just apply your map twice in your top-level macro.

Re: Recursive macros in C, demystified (once the ugly crying stops)

#23
post #8
post #6

Earlier quoted context omitted.

Author of the article here. Absolutely, the code box under the ascii art is a complete implementation, just paste that in a C file, and then use `H4X0R_VA_COUNT(...)`. Or, you could follow the link the my typed variadic arguments article (from which this post forked off). The repo there is: https://codeberg.org/h4x0r/vargs

And yes, GCC extensions are often going to be adopted in clang, but generally not the broader world of C and C++ compilers. Everything in my article conforms to the standard.

I played with a lot of preprocessor implementations and did my own (redesigned chibicc's expansion algorithm), not many of them even have paint-blue behavior exactly right (the standard text is vague, to me it was more "matching GCC's" than "conforming to standard").

Re: Recursive macros in C, demystified (once the ugly crying stops)

#24
post #16

Is this a DoS risk - code that sends your build chain into an infinite loop?

Without any specific implementation of a constraint it certainly can happen, although I'm not totally sure that it's something to be concerned about in terms of a DOS as much as a nuisance when writing code with a bug in it; if you're including malicious code, there's probably much worse things it could do if it actually builds properly instead of just spinning indefinitely. Rust's macros are recursive intentionally,…

C++ also has constexpr functions, which can be recursive.

All code can have bugs, error out and die.

There are lots of good reasons to run code at compile time, most commonly to generate code, especially tedious and error-prone code. If the language doesn't have good built-in facilities to do that, then people will write separate programs as part of the build, which adds system complexity, which is, in my experience, worse for C than for most other languages.

If a language can remove that build complexity, and the semantics are clear enough to the average programmer (For example, Nim's macro system which originally were highly appealing (and easy) to me as a compiler guy, until I saw how other people find even simple examples completely opaque-- worse than C macros.

Re: Recursive macros in C, demystified (once the ugly crying stops)

#25
post #23
post #8

Earlier quoted context omitted.

And yes, GCC extensions are often going to be adopted in clang, but generally not the broader world of C and C++ compilers. Everything in my article conforms to the standard.

I played with a lot of preprocessor implementations and did my own (redesigned chibicc's expansion algorithm), not many of them even have paint-blue behavior exactly right (the standard text is vague, to me it was more "matching GCC's" than "conforming to standard").

That's interesting. I agree with you that the standards text is pretty vague. I think that's why other attempts to show how to do this kind of thing don't get deep enough on the semantics, and why I adopted a "try it and see" strategy.

I do try to avoid this kind of thing unless necessary, so I don't have experience as to where the different compilers will fall down on different corner cases. I'd find it very interesting though, so please do share if you kept any record or have any memory!

Re: Recursive macros in C, demystified (once the ugly crying stops)

#27
post #22

Earlier quoted context omitted.

Please?

And I should say, if you want to apply the same transformation to arguments twice, but call F() separately from G() per your starting example, you'd just apply your map twice in your top-level macro.

> if you want to apply the same transformation to arguments twice, but call F() > separately from G() per your starting example, you'd just apply your map twice in your top-level macro

My brain just blew a fuse.

Re: Recursive macros in C, demystified (once the ugly crying stops)

#28
post #7

The behavior of C macros is actually described by a piece of pseudocode from Dave Prosser and it is not in the standard: * https://www.spinellis.gr/blog/20060626/ * https://www.spinellis.gr/pubs/jrnl/2006-DDJ-Finessing/html/S... * https://gcc.gnu.org/legacy-ml/gcc-prs/2001-q1/msg00495.html

Wow, I'm not sure I've ever seen this (or if I did, it was 20 years ago).

And I was definitely looking around for this kind of history when I was searching around when writing. Perhaps my google skills have decayed... or google... or both!

Thanks very much.

Re: Recursive macros in C, demystified (once the ugly crying stops)

#29
post #16

Is this a DoS risk - code that sends your build chain into an infinite loop?

Without any specific implementation of a constraint it certainly can happen, although I'm not totally sure that it's something to be concerned about in terms of a DOS as much as a nuisance when writing code with a bug in it; if you're including malicious code, there's probably much worse things it could do if it actually builds properly instead of just spinning indefinitely. Rust's macros are recursive intentionally,…

D doesn't have macros, quite deliberately.

What it does have are two features:

1. compile time evaluation of functions - meaning you can write ordinary D code and execute it at compile time, including handling strings

2. a "mixin" statement that has a string as an argument, and the string is compiled as if it were D source code, and that code replaces the mixin statement, and is compiled as usual

Simple and easy.

Post reply on HN