Thread overview
X macro in D
Aug 20, 2022
Walter Bright
Aug 20, 2022
Timon Gehr
Aug 20, 2022
Tejas
Aug 20, 2022
Adam D Ruppe
Aug 20, 2022
Paul Backus
August 20, 2022
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."

https://news.ycombinator.com/item?id=32530255

Who is up for the challenge?
August 20, 2022
On 8/20/22 15:11, 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."
> 
> https://news.ycombinator.com/item?id=32530255
> 
> Who is up for the challenge?

Not much of a challenge.

Your example:

```d
enum Color { red, blue, green }
static immutable colorStrings = [EnumMembers!Color].map!text.array;
static foreach(color;EnumMembers!Color){ ... }
```

Example 1 from Wikipedia:

```d
auto value1=1,value2=2,value3=3;
alias vars=AliasSeq!(value1,value2,value3);
void printVariables(){
    static foreach(alias var;vars){
        writeln(__traits(identifier, var), " = ", var);
    }
}
```

Example 2 from Wikipedia:

enum id1=1, id2=2, id3=3;

static immutable varList = [
    tuple("id1", "name1"),
    tuple("id2", "name2"),
    tuple("id3", "name3"),
];

static foreach(id, name; varList.map!(x=>x)){ // yuck
    mixin(`int `~name~`;`);
}

mixin(`enum MyIdListType{`~varList.map!(x=>x[1]~"="~x[0]).join(",")~`}`);

Does not allow you to add new columns transparently, but I guess we'd need actual tuple pattern matching for that (or do everything via indices).
August 20, 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.)

August 20, 2022

On Saturday, 20 August 2022 at 14:15:52 UTC, Timon Gehr wrote:

>

On 8/20/22 15:11, Walter Bright wrote:

>

[...]

Not much of a challenge.

Your example:

[...]

Are our IDEs/LSPs still capable of providing autocomplete information after all that kind of code though?

August 20, 2022
On Saturday, 20 August 2022 at 16:23:47 UTC, Tejas wrote:
> Are our IDEs/LSPs still capable of providing autocomplete information after all that kind of code though?

The beauty of the reflection approaches is the code just remains the code and works normally including the auto complete.