www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - X macro in D

reply Walter Bright <newshound2 digitalmars.com> writes:
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?
Aug 20 2022
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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).
Aug 20 2022
parent reply Tejas <notrealemail gmail.com> writes:
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?
Aug 20 2022
parent Adam D Ruppe <destructionator gmail.com> writes:
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.
Aug 20 2022
prev sibling parent Paul Backus <snarwin gmail.com> writes:
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.)
Aug 20 2022