www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The X Macro using D

reply Walter Bright <newshound2 digitalmars.com> writes:
Some time ago, I wrote about the X Macro in C:

   https://digitalmars.com/articles/b51.html

I used it from time to time in C code. It's one of the things I actually like 
about the C preprocessor. But in translating the aged C code to D it was time
to 
make X work in D. Here's the C code, followed by the D translation.

(I suppose it could be done with C++ templates, but I'll leave that to Andrei
or 
Eric Niebler <g>.)

================ C Version ================

// Macro trick to generate several parallel tables

#define Y \
         X("AH",4,mAX,TYuchar)   \
         X("AL",0,mAX,TYuchar)   \
         X("AX",8,mAX,TYushort)  \
         X("BH",7,mBX,TYuchar)   \
         X("BL",3,mBX,TYuchar)   \
         X("BP",13,0,TYushort)   \
         X("BX",11,mBX,TYushort) \
         X("CH",5,mCX,TYuchar)   \
         X("CL",1,mCX,TYuchar)   \
         X("CX",9,mCX,TYushort)  \
         X("DH",6,mDX,TYuchar)   \
         X("DI",15,mDI,TYushort) \
         X("DL",2,mDX,TYuchar)   \
         X("DX",10,mDX,TYushort) \
         X("EAX",16,mAX,TYulong) \
         X("EBP",21,0,TYulong)   \
         X("EBX",19,mBX,TYulong) \
         X("ECX",17,mCX,TYulong) \
         X("EDI",23,mDI,TYulong) \
         X("EDX",18,mDX,TYulong) \
         X("ESI",22,mSI,TYulong) \
         X("ESP",20,0,TYulong)   \
         X("SI",14,mSI,TYushort) \
         X("SP",12,0,TYushort)

// Table for identifiers
static const char *pseudotab[] =
{
#define X(id,reg,m,ty)  id,
         Y
#undef X
};

// Register number to use in addressing mode
unsigned char pseudoreg[] =
{
#define X(id,reg,m,ty)  reg,
         Y
#undef X
};

// Mask to use for registers affected
regm_t pseudomask[] =
{
#define X(id,reg,m,ty)  m,
         Y
#undef X
};

// Table for type of pseudo register variable
static unsigned char pseudoty[] =
{
#define X(id,reg,m,ty)  mTYvolatile | ty,
         Y
#undef X
};

================ D Version ================

/* 4 parallel tables using "X Macro" technique
  */

template Y(alias X)
{
     enum Y =
     [
         //  id   reg  mask   ty
         X!("AH",   4, mAX, TYuchar),
         X!("AL",   0, mAX, TYuchar),
         X!("AX",   8, mAX, TYushort),
         X!("BH",   7, mBX, TYuchar),
         X!("BL",   3, mBX, TYuchar),
         X!("BP",  13,   0, TYushort),
         X!("BX",  11, mBX, TYushort),
         X!("CH",   5, mCX, TYuchar),
         X!("CL",   1, mCX, TYuchar),
         X!("CX",   9, mCX, TYushort),
         X!("DH",   6, mDX, TYuchar),
         X!("DI",  15, mDI, TYushort),
         X!("DL",   2, mDX, TYuchar),
         X!("DX",  10, mDX, TYushort),
         X!("EAX", 16, mAX, TYulong),
         X!("EBP", 21,   0, TYulong),
         X!("EBX", 19, mBX, TYulong),
         X!("ECX", 17, mCX, TYulong),
         X!("EDI", 23, mDI, TYulong),
         X!("EDX", 18, mDX, TYulong),
         X!("ESI", 22, mSI, TYulong),
         X!("ESP", 20,   0, TYulong),
         X!("SI",  14, mSI, TYushort),
         X!("SP",  12,   0, TYushort),
     ];
}

// Table for identifiers

template Xtab(alias A, alias B, alias C, alias D) { enum Xtab = A; }

private __gshared const(char)*[24] pseudotab = Y!Xtab;


// Register number to use in addressing mode

template Xreg(alias A, alias B, alias C, alias D) { enum Xreg = B; }

__gshared ubyte[24] pseudoreg = Y!Xreg;


// Mask to use for registers affected

template Xmask(alias A, alias B, alias C, alias D) { enum Xmask = C; }

__gshared regm_t[24] pseudomask = Y!Xmask;


// Table for type of pseudo register variable

template Xty(alias A, alias B, alias C, alias D) { enum Xty = mTYvolatile | D; }

private __gshared const(tym_t)[24] pseudoty = Y!Xty;
Jul 20 2017
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 20 July 2017 at 21:17:45 UTC, Walter Bright wrote:
 template Y(alias X)
 {
     enum Y =
     [
         //  id   reg  mask   ty
         X!("AH",   4, mAX, TYuchar),
         X!("AL",   0, mAX, TYuchar),
         X!("AX",   8, mAX, TYushort),
         X!("BH",   7, mBX, TYuchar),
         X!("BL",   3, mBX, TYuchar),
         X!("BP",  13,   0, TYushort),
         X!("BX",  11, mBX, TYushort),
         X!("CH",   5, mCX, TYuchar),
         X!("CL",   1, mCX, TYuchar),
         X!("CX",   9, mCX, TYushort),
         X!("DH",   6, mDX, TYuchar),
         X!("DI",  15, mDI, TYushort),
         X!("DL",   2, mDX, TYuchar),
         X!("DX",  10, mDX, TYushort),
         X!("EAX", 16, mAX, TYulong),
         X!("EBP", 21,   0, TYulong),
         X!("EBX", 19, mBX, TYulong),
         X!("ECX", 17, mCX, TYulong),
         X!("EDI", 23, mDI, TYulong),
         X!("EDX", 18, mDX, TYulong),
         X!("ESI", 22, mSI, TYulong),
         X!("ESP", 20,   0, TYulong),
         X!("SI",  14, mSI, TYushort),
         X!("SP",  12,   0, TYushort),
     ];
 }

 // Table for identifiers

 template Xtab(alias A, alias B, alias C, alias D) { enum Xtab = 
 A; }

 private __gshared const(char)*[24] pseudotab = Y!Xtab;


 // Register number to use in addressing mode

 template Xreg(alias A, alias B, alias C, alias D) { enum Xreg = 
 B; }

 __gshared ubyte[24] pseudoreg = Y!Xreg;


 // Mask to use for registers affected

 template Xmask(alias A, alias B, alias C, alias D) { enum Xmask 
 = C; }

 __gshared regm_t[24] pseudomask = Y!Xmask;


 // Table for type of pseudo register variable

 template Xty(alias A, alias B, alias C, alias D) { enum Xty = 
 mTYvolatile | D; }

 private __gshared const(tym_t)[24] pseudoty = Y!Xty;
Please tell me this is not going to get into dmd :) templates are so much more expensive then macros. (Well, for now :) ) Those templates can and should be replaced by CTFE.
Jul 20 2017
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/20/2017 2:21 PM, Stefan Koch wrote:
 Please tell me this is not going to get into dmd :)
 templates are so much more expensive then macros.
 (Well, for now :) )
 
 Those templates can and should be replaced by CTFE.
If you like, present the CTFE solution. Should be fun!
Jul 20 2017
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 20 July 2017 at 22:02:32 UTC, Walter Bright wrote:
 On 7/20/2017 2:21 PM, Stefan Koch wrote:
 Please tell me this is not going to get into dmd :)
 templates are so much more expensive then macros.
 (Well, for now :) )
 
 Those templates can and should be replaced by CTFE.
If you like, present the CTFE solution. Should be fun!
My pleasure :) string itos(uint n) { char[] result = []; immutable len = 10; result.length = len; uint i = len - 1; while (n > 10) { result[i--] = cast(char) ('0' + (n % 10)); n /= 10; } result[i] = cast(char) ('0' + (n % 10)); return cast(string) result[i .. $]; } mixin((){ static struct X { string id; ubyte reg; uint mask; ubyte ty; } enum Y = [ // id reg mask ty X("AH", 4, mAX, TYuchar), X("AL", 0, mAX, TYuchar), X("AX", 8, mAX, TYushort), X("BH", 7, mBX, TYuchar), X("BL", 3, mBX, TYuchar), X("BP", 13, 0, TYushort), X("BX", 11, mBX, TYushort), X("CH", 5, mCX, TYuchar), X("CL", 1, mCX, TYuchar), X("CX", 9, mCX, TYushort), X("DH", 6, mDX, TYuchar), X("DI", 15, mDI, TYushort), X("DL", 2, mDX, TYuchar), X("DX", 10, mDX, TYushort), X("EAX", 16, mAX, TYulong), X("EBP", 21, 0, TYulong), X("EBX", 19, mBX, TYulong), X("ECX", 17, mCX, TYulong), X("EDI", 23, mDI, TYulong), X("EDX", 18, mDX, TYulong), X("ESI", 22, mSI, TYulong), X("ESP", 20, 0, TYulong), X("SI", 14, mSI, TYushort), X("SP", 12, 0, TYushort), ]; enum lns = itos(Y.length); string pseudotab = "\nprivate __gshared static immutable string[" ~ lns ~ "] pseudotab = ["; string pseudoreg = "\nprivate __gshared static immutable ubyte[" ~ lns ~ "] pseudoreg = ["; string pseudomask = "\nprivate __gshared static immutable regm_t[" ~ lns ~ "] pseudomask = ["; string pseudoty = "\nprivate __gshared static immutable ubyte[" ~ lns ~ "] pseudoty = ["; foreach(i, r; Y) { pseudotab ~= `"` ~ r.id ~ `", `; pseudoreg ~= itos(r.reg) ~ `, `; pseudomask ~= itos(r.mask) ~ `, `; pseudoty ~= itos(r.ty) ~ `, `; } pseudotab ~= "];\n"; pseudoreg ~= "];\n"; pseudomask ~= "];\n"; pseudoty ~= "];\n"; return pseudotab ~ pseudoreg ~ pseudomask ~ pseudoty; }());
Jul 21 2017
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2017-07-21 10:06, Stefan Koch wrote:

 My pleasure :)
My approach, without string mixin: http://forum.dlang.org/post/oksd27$1li9$1 digitalmars.com :) -- /Jacob Carlborg
Jul 21 2017
prev sibling parent reply Olivier FAURE <olivier.faure epitech.eu> writes:
On Friday, 21 July 2017 at 08:06:09 UTC, Stefan Koch wrote:
 My pleasure :)

 // ...

 mixin((){
     // ...

     enum Y = [
         //  id   reg  mask   ty
         X("AH",   4, mAX, TYuchar),
         X("AL",   0, mAX, TYuchar),
         X("AX",   8, mAX, TYushort),
         X("BH",   7, mBX, TYuchar),
         X("BL",   3, mBX, TYuchar),
         // ...
         X("ESI", 22, mSI, TYulong),
         X("ESP", 20,   0, TYulong),
         X("SI",  14, mSI, TYushort),
         X("SP",  12,   0, TYushort),
     ];


     enum lns = itos(Y.length);

     string pseudotab = "\nprivate __gshared static immutable 
 string[" ~ lns ~ "] pseudotab = [";

     foreach(i, r; Y)
     {
         pseudotab ~= `"` ~ r.id ~ `", `;
     }

     pseudotab ~= "];\n";

 }());
Quick questions: isn't it possible to do private __gshared const(char)*[24] pseudotab = Y.map!(x => x.id); instead? That seems like the most obvious and easy to read option; and in contrast to the other solutions proposed, it's closer to the Rule of Least Power.
Jul 21 2017
next sibling parent Olivier FAURE <olivier.faure epitech.eu> writes:
On Friday, 21 July 2017 at 12:27:35 UTC, Olivier FAURE wrote:
     private __gshared const(char)*[24] pseudotab = Y.map!(x => 
 x.id);
I meant private __gshared static immutable string[Y.length] pseudotab = Y.map!(x => x.id); but you get my point. Also, upon trying it, it doesn't seem to work (at least the immutable part doesn't), but I don't really understand why. All the variables in the expression are known at compile time.
Jul 21 2017
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2017-07-21 14:27, Olivier FAURE wrote:

 Quick questions: isn't it possible to do

      private __gshared const(char)*[24] pseudotab = Y.map!(x => x.id);

 instead? That seems like the most obvious and easy to read option; and
 in contrast to the other solutions proposed, it's closer to the Rule of
 Least Power.
Yes, that's basically what my solution is doing [1]. [1] http://forum.dlang.org/post/oksd27$1li9$1 digitalmars.com -- /Jacob Carlborg
Jul 21 2017
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2017-07-21 00:02, Walter Bright wrote:
 On 7/20/2017 2:21 PM, Stefan Koch wrote:
 Please tell me this is not going to get into dmd :)
 templates are so much more expensive then macros.
 (Well, for now :) )

 Those templates can and should be replaced by CTFE.
If you like, present the CTFE solution. Should be fun!
Here's my solution. It sill uses templates for the implementation of "map": auto map(alias func)(const(Row)[] array) { alias R = typeof(func(Row.init)); R[] result; foreach (e ; array) result ~= func(e); return result; } struct Row { string id; int reg; int mask; int ty; } immutable Row[2] table = [ Row("AH", 4, mAX, TYuchar), Row("AL", 0, mAX, TYuchar) ]; alias regm_t = int; alias tym_t = int; enum mAX = 1; enum TYuchar = 1; enum mTYvolatile = 2; private __gshared const(char)*[table.length] pseudotab = table.map!(row => row.id); __gshared ubyte[table.length] pseudoreg = table.map!(row => row.reg); __gshared int[table.length] pseudomask = table.map!(row => row.mask); private __gshared const(tym_t)[table.length] pseudoty = table.map!(row => mTYvolatile | row.ty); I added some type aliases and enums to be able to run the code self contained without DMD. Some advantages: * Less use of templates * More readable since there's a specific type (Row) with named fields, no need to write the fields in comments * Easier to update when the length of the arrays are not hard coded -- /Jacob Carlborg
Jul 21 2017
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 21 July 2017 at 08:12:55 UTC, Jacob Carlborg wrote:
 On 2017-07-21 00:02, Walter Bright wrote:
 On 7/20/2017 2:21 PM, Stefan Koch wrote:
 Please tell me this is not going to get into dmd :)
 templates are so much more expensive then macros.
 (Well, for now :) )

 Those templates can and should be replaced by CTFE.
If you like, present the CTFE solution. Should be fun!
Here's my solution. It sill uses templates for the implementation of "map":
that uses more compile-time then mine :) and leaves bloat in the binary.
Jul 21 2017
parent Jacob Carlborg <doob me.com> writes:
On 2017-07-21 10:25, Stefan Koch wrote:

 and leaves bloat in the binary.
Perhaps that should be fixed in the compiler ;) -- /Jacob Carlborg
Jul 21 2017
prev sibling parent reply Enamex <enamex+d outlook.com> writes:
On Thursday, 20 July 2017 at 22:02:32 UTC, Walter Bright wrote:
 On 7/20/2017 2:21 PM, Stefan Koch wrote:
 Please tell me this is not going to get into dmd :)
 templates are so much more expensive then macros.
 (Well, for now :) )
 
 Those templates can and should be replaced by CTFE.
If you like, present the CTFE solution. Should be fun!
How about this (if I'm not mistaken, this's only one template instantiation per tuple-type&extracted-index): ```d import std.typecons: tuple, Tuple; import std.algorithm: map; import std.array: array; enum regm_t { AX, BX, CX, DX, DI, SI, None } enum tym_t { uchar_, ushort_, ulong_ } enum Ydata = [ tuple("AH", 4, regm_t.AX, tym_t.uchar_), tuple("AL", 0, regm_t.AX, tym_t.uchar_), tuple("AX", 8, regm_t.AX, tym_t.ushort_), tuple("BH", 7, regm_t.BX, tym_t.uchar_), tuple("BL", 3, regm_t.BX, tym_t.uchar_), tuple("BP", 13, regm_t.None, tym_t.ushort_), tuple("BX", 11, regm_t.BX, tym_t.ushort_), tuple("CH", 5, regm_t.CX, tym_t.uchar_), tuple("CL", 1, regm_t.CX, tym_t.uchar_), tuple("CX", 9, regm_t.CX, tym_t.ushort_), tuple("DH", 6, regm_t.DX, tym_t.uchar_), tuple("DI", 15, regm_t.DI, tym_t.ushort_), tuple("DL", 2, regm_t.DX, tym_t.uchar_), tuple("DX", 10, regm_t.DX, tym_t.ushort_), tuple("EAX", 16, regm_t.AX, tym_t.ulong_), tuple("EBP", 21, regm_t.None, tym_t.ulong_), tuple("EBX", 19, regm_t.BX, tym_t.ulong_), tuple("ECX", 17, regm_t.CX, tym_t.ulong_), tuple("EDI", 23, regm_t.DI, tym_t.ulong_), tuple("EDX", 18, regm_t.DX, tym_t.ulong_), tuple("ESI", 22, regm_t.SI, tym_t.ulong_), tuple("ESP", 20, regm_t.None, tym_t.ulong_), tuple("SI", 14, regm_t.SI, tym_t.ushort_), tuple("SP", 12, regm_t.None, tym_t.ushort_), ]; static auto Y(size_t idx, T...)(Tuple!(T)[] ts) pure nothrow { // I thought to try something like assumeUnique here // but was thinking of the Rust semantics in doing so // Not sure if this leads to spurious allocations at // the points where Y is used. Is there someway to tell them, // even if the target type is const or immutable // that this returned array is brand new with no other references // around? return ts .map!(x => x[idx]) .array ; } enum { Xtab = 0, Xreg, Xmask, Xty, } // Register number to use in addressing mode private __gshared const(char)*[24] pseudotab = Y!Xtab(Ydata); // Register number to use in addressing mode __gshared ubyte[24] pseudoreg = Y!Xreg(Ydata); // Mask to use for registers affected __gshared regm_t[24] pseudomask = Y!Xmask(Ydata); // Table for type of pseudo register variable private __gshared const(tym_t)[24] pseudoty = Y!Xty(Ydata); ```
Jul 21 2017
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 21 July 2017 at 20:44:13 UTC, Enamex wrote:
 On Thursday, 20 July 2017 at 22:02:32 UTC, Walter Bright wrote:
 [...]
How about this (if I'm not mistaken, this's only one template instantiation per tuple-type&extracted-index): [...]
tuple map and array are all pretty expensive. please profile.
Jul 22 2017
parent Martin Nowak <code dawg.eu> writes:
On Saturday, 22 July 2017 at 11:50:40 UTC, Stefan Koch wrote:
 tuple map and array are all pretty expensive.

 please profile.
Well a bit more compile time isn't the end of the world, and by far not the only metric (e.g. readability and maintainability also rank high). You're slightly obsessed with the template compile-time topic ;). BTW, the term expensive is fairly subjective easily misunderstood. Sth. more specific would reduce the chance for confusion, e.g. "tuple map and array require longer to compile"
Jul 22 2017
prev sibling next sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 20 July 2017 at 21:17:45 UTC, Walter Bright wrote:
 Some time ago, I wrote about the X Macro in C:

   https://digitalmars.com/articles/b51.html

 I used it from time to time in C code. It's one of the things I 
 actually like about the C preprocessor. But in translating the 
 aged C code to D it was time to make X work in D. Here's the C 
 code, followed by the D translation.
In C there's no point in the X macro anymore since C99. Designated initializer allow to do it properly[1] now. enum COLORS { red, blue, green, max }; char *cstring[max] = {[red]="red", [blue]="blue", [green]="green" }; /* C99 */ It works also with array indexes[2]. int a[3] = { [2]=1, [0]=3, [1]=2 }; /* C99 designated initializer */ int a[3] = { [2]=1, [0]=3, 2 }; /* C99 designated initializer */ C++ hasn't yet integrated. [1]: https://dlang.org/ctod.html#arrayenum [2]: https://dlang.org/ctod.html#arrayinit2
Jul 21 2017
parent Nick Treleaven <nick geany.org> writes:
On Friday, 21 July 2017 at 11:19:47 UTC, Patrick Schluter wrote:
 In C there's no point in the X macro anymore since C99.
 Designated initializer allow to do it properly[1] now.

     enum COLORS { red, blue, green, max };
     char *cstring[max] = {[red]="red", [blue]="blue", 
 [green]="green" };  /* C99 */
I don't see how this allows data for different arrays to be written in interleaved form and then extracted by column to initialize the separate arrays, as Walter's does.
Jul 21 2017
prev sibling next sibling parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Thursday, 20 July 2017 at 21:17:45 UTC, Walter Bright wrote:
 Some time ago, I wrote about the X Macro in C:

   https://digitalmars.com/articles/b51.html

 I used it from time to time in C code. It's one of the things I 
 actually like about the C preprocessor. But in translating the 
 aged C code to D it was time to make X work in D. Here's the C 
 code, followed by the D translation.

 (I suppose it could be done with C++ templates, but I'll leave 
 that to Andrei or Eric Niebler <g>.)

 ================ C Version ================

 // Macro trick to generate several parallel tables

 #define Y \
         X("AH",4,mAX,TYuchar)   \
         X("AL",0,mAX,TYuchar)   \
         X("AX",8,mAX,TYushort)  \
         X("BH",7,mBX,TYuchar)   \
         X("BL",3,mBX,TYuchar)   \
         X("BP",13,0,TYushort)   \
         X("BX",11,mBX,TYushort) \
         X("CH",5,mCX,TYuchar)   \
         X("CL",1,mCX,TYuchar)   \
         X("CX",9,mCX,TYushort)  \
         X("DH",6,mDX,TYuchar)   \
         X("DI",15,mDI,TYushort) \
         X("DL",2,mDX,TYuchar)   \
         X("DX",10,mDX,TYushort) \
         X("EAX",16,mAX,TYulong) \
         X("EBP",21,0,TYulong)   \
         X("EBX",19,mBX,TYulong) \
         X("ECX",17,mCX,TYulong) \
         X("EDI",23,mDI,TYulong) \
         X("EDX",18,mDX,TYulong) \
         X("ESI",22,mSI,TYulong) \
         X("ESP",20,0,TYulong)   \
         X("SI",14,mSI,TYushort) \
         X("SP",12,0,TYushort)

 // Table for identifiers
 static const char *pseudotab[] =
 {
 #define X(id,reg,m,ty)  id,
         Y
 #undef X
 };

 // Register number to use in addressing mode
 unsigned char pseudoreg[] =
 {
 #define X(id,reg,m,ty)  reg,
         Y
 #undef X
 };

 // Mask to use for registers affected
 regm_t pseudomask[] =
 {
 #define X(id,reg,m,ty)  m,
         Y
 #undef X
 };

 // Table for type of pseudo register variable
 static unsigned char pseudoty[] =
 {
 #define X(id,reg,m,ty)  mTYvolatile | ty,
         Y
 #undef X
 };

 ================ D Version ================

 /* 4 parallel tables using "X Macro" technique
  */

 template Y(alias X)
 {
     enum Y =
     [
         //  id   reg  mask   ty
         X!("AH",   4, mAX, TYuchar),
         X!("AL",   0, mAX, TYuchar),
         X!("AX",   8, mAX, TYushort),
         X!("BH",   7, mBX, TYuchar),
         X!("BL",   3, mBX, TYuchar),
         X!("BP",  13,   0, TYushort),
         X!("BX",  11, mBX, TYushort),
         X!("CH",   5, mCX, TYuchar),
         X!("CL",   1, mCX, TYuchar),
         X!("CX",   9, mCX, TYushort),
         X!("DH",   6, mDX, TYuchar),
         X!("DI",  15, mDI, TYushort),
         X!("DL",   2, mDX, TYuchar),
         X!("DX",  10, mDX, TYushort),
         X!("EAX", 16, mAX, TYulong),
         X!("EBP", 21,   0, TYulong),
         X!("EBX", 19, mBX, TYulong),
         X!("ECX", 17, mCX, TYulong),
         X!("EDI", 23, mDI, TYulong),
         X!("EDX", 18, mDX, TYulong),
         X!("ESI", 22, mSI, TYulong),
         X!("ESP", 20,   0, TYulong),
         X!("SI",  14, mSI, TYushort),
         X!("SP",  12,   0, TYushort),
     ];
 }

 // Table for identifiers

 template Xtab(alias A, alias B, alias C, alias D) { enum Xtab = 
 A; }

 private __gshared const(char)*[24] pseudotab = Y!Xtab;


 // Register number to use in addressing mode

 template Xreg(alias A, alias B, alias C, alias D) { enum Xreg = 
 B; }

 __gshared ubyte[24] pseudoreg = Y!Xreg;


 // Mask to use for registers affected

 template Xmask(alias A, alias B, alias C, alias D) { enum Xmask 
 = C; }

 __gshared regm_t[24] pseudomask = Y!Xmask;


 // Table for type of pseudo register variable

 template Xty(alias A, alias B, alias C, alias D) { enum Xty = 
 mTYvolatile | D; }

 private __gshared const(tym_t)[24] pseudoty = Y!Xty;
I wonder if you could use one of the SoA implementations (e.g from here: https://wiki.dlang.org/Transforming_slice_of_structs_into_struct_of_slices) to the same effect.
Jul 21 2017
prev sibling parent reply Johan Engelen <j j.nl> writes:
On Thursday, 20 July 2017 at 21:17:45 UTC, Walter Bright wrote:
 Some time ago, I wrote about the X Macro in C:

   https://digitalmars.com/articles/b51.html

 I used it from time to time in C code. It's one of the things I 
 actually like about the C preprocessor. But in translating the 
 aged C code to D it was time to make X work in D. Here's the C 
 code, followed by the D translation.
This mechanism is used in LLVM in a number of places, where the list entries are used e.g. to populate tables and to define aggregate member fields. For example: https://github.com/llvm-mirror/compiler-rt/blob/master/lib/profile/InstrProfData.inc#L29-L51
Jul 21 2017
parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Friday, 21 July 2017 at 19:26:05 UTC, Johan Engelen wrote:
 On Thursday, 20 July 2017 at 21:17:45 UTC, Walter Bright wrote:
 Some time ago, I wrote about the X Macro in C:

   https://digitalmars.com/articles/b51.html

 I used it from time to time in C code. It's one of the things 
 I actually like about the C preprocessor. But in translating 
 the aged C code to D it was time to make X work in D. Here's 
 the C code, followed by the D translation.
This mechanism is used in LLVM in a number of places, where the list entries are used e.g. to populate tables and to define aggregate member fields. For example: https://github.com/llvm-mirror/compiler-rt/blob/master/lib/profile/InstrProfData.inc#L29-L51
And those in turn are generated from TableGen.
Jul 21 2017