X macro in D

Paul Backus snarwin at gmail.com
Sat Aug 20 14:20:00 UTC 2022


On Saturday, 20 August 2022 at 13:11:43 UTC, Walter Bright wrote:
> The X macro in C is famous:
>
> https://www.digitalmars.com/articles/b51.html
>
> Here's the challenge:
>
> "X-macros in C [1] are great for doing compile-time code 
> generation from tabular data. Especially for things like 
> pinouts and their respective capabilities on microcontrollers, 
> I haven't seen any other language give such a succinct, easily 
> maintainable way of interacting and representing data such that 
> it can be used for full function generation that an IDE can 
> autocomplete names, types etc.
> Sure there are other "newer" ways but they invariably involve 
> so much boilerplate that you might as well just write all the 
> code by hand."

Here's an implementation of the color example in D:

     import std.traits, std.meta;

     enum Color { red, blue, green }

     enum getName(alias sym) = __traits(identifier, sym);
     string[] colorStrings = [staticMap!(getName, 
EnumMembers!Color)];

     unittest
     {
         assert(colorStrings[Color.red] == "red");
         assert(colorStrings[Color.blue] == "blue");
         assert(colorStrings[Color.green] == "green");
     }

Since we have access to powerful compile-time reflection in D, we 
can let the enum be the "source of truth", and just generate the 
array.

Ok, but what if we want to use names for the enum members that 
aren't identical to their string representation? Simple:

     import std.traits, std.meta;

     struct name { string str; }

     enum Color
     {
         @name("red") Cred,
         @name("blue") Cblue,
         @name("green") Cgreen
     }

     enum getName(alias sym) = getUDAs!(sym, name)[0].str;
     string[] colorStrings = [staticMap!(getName, 
EnumMembers!Color)];

     unittest
     {
         assert(colorStrings[Color.Cred] == "red");
         assert(colorStrings[Color.Cblue] == "blue");
         assert(colorStrings[Color.Cgreen] == "green");
     }

In C, each additional "column" of data is represented as an 
additional argument to the X macro. Since D's UDAs let us attach 
arbitrary data to any symbol, including enum members, we can turn 
those additional arguments into attributes of the enum members, 
and once again use the enum itself as the source of truth.

(These examples also show off the utility of D's built-in unit 
tests, for checking our work.)


More information about the Digitalmars-d mailing list