X Macros are a classic C++ pattern. Less known than some of the more popular techniques like curiously recurring template pattern, but still quite common in large codebases.<p>(Although... it <i>is</i> a neat trick, but... It is kind of mostly useful <i>because</i> C++ macros are not very powerful. If they were more powerful, most uses of X Macros could be replaced by just having a single macro do all of the magic.)<p>I recently saw something I hadn't seen before in a similar vein. There's a million different bin2c generators, and most of them will generate a declaration for you. However, I just saw a setup where the bin2c conversion just generates the actual byte array text, e.g. "0x00, 0x01, ..." so that you could #include it into whatever declaration you want. Of course, this is basically a poor man's #embed, but I found it intriguing nonetheless. (It's nice that programming languages have been adding file embedding as a first-class feature. This really simplifies a lot of stuff and removes the need to rely on more complex solutions for problems that don't really warrant them.)
This is really more a C pattern than a C++ pattern, isn't it?<p>Frustrated by C's limitations relative to Golang, last year I sketched out an approach to using X-macros to define VNC protocol message types as structs with automatically-generated serialization and deserialization code. So for example you would define the VNC KeyEvent message as follows:<p><pre><code> #define KeyEvent_fields(field, padding) \
field(u8, down_flag) \
padding(u8) \
padding(u8) \
field(u32, keysym)
MESSAGE_TYPE(KeyEvent)
</code></pre>
And that would generate a KeyEvent typedef (to an anonymous struct type) and two functions named read_KeyEvent_big_endian and write_KeyEvent_big_endian. (It wouldn't be difficult to add debug_print_KeyEvent.) Since KeyEvent is a typedef, you can use it as a field type in other, larger structs just like u8 and u32.<p>Note that here there are two Xes, and they are passed as parameters to the KeyEvent_fields macro rather than being globally defined and undefined over time. To me this feels cleaner than the traditional way.<p>The usage above is in <a href="http://canonical.org/~kragen/sw/dev3/binmsg_cpp.c" rel="nofollow">http://canonical.org/~kragen/sw/dev3/binmsg_cpp.c</a>, MESSAGE_TYPE and its ilk are in <a href="http://canonical.org/~kragen/sw/dev3/binmsg_cpp.h" rel="nofollow">http://canonical.org/~kragen/sw/dev3/binmsg_cpp.h</a>, and an alternative approach using Python instead of the C preprocessor to generate the required C is in <a href="http://canonical.org/~kragen/sw/dev3/binmsg.py" rel="nofollow">http://canonical.org/~kragen/sw/dev3/binmsg.py</a>.
I hate X-Macros, but they're very useful in some situations. We use them to generate an enum of error values and also a string table of those names. (Unlike two separate tables, the X macro version is guaranteed to be the exact correct length / values align with the corresponding string.)
I understand the use case of this, but when I see it I always wonder if, and think I would prefer, some external code generation step instead rather than falling back on macros in the preprocessor. Like an external script or something.
"X" macros are great until you need two of them visible in the same translation unit. It is much better to pass a list macro as an argument to a uniquely named X macro and avoid the need to ever undef anything.
Some minor tweaks to what the author shows to make it even better.<p>Give the macro a more descriptive name. For their example, call it GLOBAL_STRING instead of X. I think this helps make things clearer.<p>#undef the macro at the end of the header. That removes one line of boilerplate from every use of the header.<p>Use #ifndef at the top of the header and emit a nice error if the macro isn't defined. This will make it easier to understand what's wrong if you forget the #define or misspell the macro.
I've learned about them, staring endlessly at the luajit source code - for example (not the best example I can remember, but still) - <a href="https://github.com/LuaJIT/LuaJIT/blob/v2.1/src/lj_lex.h#L15" rel="nofollow">https://github.com/LuaJIT/LuaJIT/blob/v2.1/src/lj_lex.h#L15</a><p>there it defines a TOKEN(_,__) macro generator the luajit token/keywords and later generates enums with them.<p>I've used it recently to wrap a "C" api with lots of functions, such that I can redirect calls.
I can't believe this actually has a name (is this an attempt to make fetch happen?) and it's not considered an anti-pattern. This sort of preprocessor/code mixing is impossible to debug and maintain; most senior C++ programmers have advised people to avoid doing this since the 1990's.<p>There is a role for the preprocessor to automate simple tables in C and such, but anything complex should be avoided like the plague if you don't like tech debt. And there's not much excuse for using it in C++ to this extent.
It is a clever trick. Very useful in C also, maybe more than in C++.<p>It can be overused, though.<p>Kind of works like Rust declarative macros (`macro_rules!`) in that it is often used to repeat and expand something common across an axis of things that vary.<p>It's funny that the simple name X Macros has stuck and is a de facto standard. E.g. <a href="https://en.wikipedia.org/wiki/X_macro" rel="nofollow">https://en.wikipedia.org/wiki/X_macro</a> suggests they date to the 1960s.
Famous in IBM 360 assembly language, in particular these were used for building code based on data schemas in CICS [1] applications which were remarkably similar to 2000-era cgi-bin applications in that you drew forms on 3270 terminals [2] and instead of sending a character for each keystroke like the typical minicomputer terminal, users would hit a button to submit the form, like an HTML form, and the application mostly shuttled data between forms and the database.<p>[1] <a href="https://en.wikipedia.org/wiki/CICS" rel="nofollow">https://en.wikipedia.org/wiki/CICS</a><p>[2] <a href="https://en.wikipedia.org/wiki/IBM_3270" rel="nofollow">https://en.wikipedia.org/wiki/IBM_3270</a>
One the one hand it is great and probably usable to solve actual problems.<p>On the other hand it seems fishy that you need all these hacks to do it. There must be a way simpler language than C++ where you could do the same easier.
i hate this "trick" with a passion. i am the one who winds up having to debug this shit, and theres nothing i love doing more than trawling through hundreds of thousands of preprocessed C++ that gets defecated by `gcc -E ...`. even better when the macro expansion is 10 levels deep.<p>you need tables? use python (or your scripting language of choice) as a pre-compile step that generates the .cpp/.c/.h files, and then build <i>that</i>.