Typo: in the user code example, I think the second should be in rather than out.
This is pretty cute - looks useful in practice. I think you can slightly enhance the probability of the compiler making the result efficient by using a switch statement, like:
The "linked list on the stack" mechanism can be used even outside of metaprogramming context.
Imagine you are traversing a binary tree and while doing so, you want to keep some data associated with every ancestor node of the one being processed at the moment:
Doesn't libcurl encourage allocating linked lists on the stack for passing options to functions? IIRC a lot of functions take too many options to be practically specified as arguments so they have a datatype something like
A common optimization in hierarchical trees is to have each node actually hold multiple levels. This can be especially efficient if the node data can fit as a small multiple of the size of a cache line (64 bytes on modern Intel processors).
I was surprised to see that the linked "real world" implementation actually defines a bunch of macros with very generic names like "in", "out" and "end".
The "standard" way of doing these things would be to name them MILL_IN etc. Not as pretty, but less likely to cause problems down the road.
Now you made me curios. How would you go about doing it with functions? Keep in mind that the goal is to interleave conditions and the code in switch-like way.
The goal is to implement the desired functionality in a comprehensible way. Given that, the best option would be to use an array, and to initialize it with the easy array intialization syntax that C provides:
It's easy to be better than cpp, but without the ability to inspect the AST rather than raw text, you can't do the really cool things that macros in languages from Scheme to Rust can. (I've also heard bad things about m4's usability, though I haven't learned to use it myself.) If anyone wants to try to retrofit such functionality into C programs and is willing to add an extra command to the build (as m4 would require), I'd suggest implementing the macros in Python and using pycparser - or using some language's clang bindings or other compiler interfaces, but that has the disadvantage of more dependencies.
In fact, python and C being my favorite two languages, and I'm trying to do some metaprogramming in some python-C hybrid way, I'd be very interested in looking into this.
The other thing I'm spending a lot of time on these days is 'TeX' (specifically LaTeX but I've explored the internals of the system a bit). As TeX is also a macro system, which probably could be made to work as a general-purpose macro system, I'm interested in how cpp, m4, TeX, and lisp/scheme macros compare and contrast.
I've had some success doing the meta programming in python, which generates .cpp (since the preprocessor does some expansion, it's not C++) files that I include from and .h file which I include in C code. It's harder to go the other way. It lets you basically modify only the python source and then have the same names for structs (classes in python) in both languages.
But then I realized it was all pretty complicated. So I started using ctypes instead. That way I only wrote it once in a header file. The only real limitation when using cpython was that I could only include a maximum of one other struct in each struct, so sometimes the C header files were a bit strangely written.
This is pretty cute - looks useful in practice. I think you can slightly enhance the probability of the compiler making the result efficient by using a switch statement, like:
etc.In C++, you could do something fairly equivalent with lambdas, without having to involve the "evil" preprocessor.