Combining structs
This recipe is essentially a workaround for the lack of availability of multiple alias this. The code shows how a template is used to generate a struct S_ABC from multiple structs S_A, S_B and S_C. The template automatically generates properties with which the fields of S_A, S_B and S_C can be directly accessed from S_ABC. Run the code
Program Code
import std.traits : isIntegral, isFloatingPoint, isCallable,
PIT=ParameterIdentifierTuple, PTT=ParameterTypeTuple;
import std.conv : to ;
import std.algorithm : startsWith ;
import std.array : split ;
//import std.stdio : writeln ;
template GenStructs(uint count, U...)
{
static if (U.length == 0)
{
enum GenStructs = "\n";
}
else
{
enum GenStructs = "\n" ~ (U[0]).stringof ~ " _s" ~ to!string(count) ~ ";" ~
GenStructs!(1+count, U[1..$]);
}
//pragma(msg, GenStructs);
}
/*
struct test0 { }
struct test1(T)
{
T A;
this(T A_) { A = A_; }
void setA(T A_) { A = A_; }
T getA() { return A; }
void empty() { writeln("empty() called"); }
~this() { }
}
*/
//pragma(msg, GenStructs!(0, test0, test1!(int)) );
//pragma(msg, __traits(allMembers, test1!(int)));
template Filter(U...)
{
static if (U.length == 0)
{
enum Filter = "";
}
else
{
static if ( (U[0]).startsWith("__") || (U[0]).startsWith("op") )
{
enum Filter = Filter!(U[1..$]); //skip U[0]
}
else
{
enum Filter = U[0] ~ "," ~ Filter!(U[1..$]);
}
}
}
//pragma(msg, Filter!(__traits(allMembers, test1!(int)))[0..$-1].split(",") );
template StringOf(TS...)
{
static if(TS.length == 0)
{
enum StringOf = "";
}
else static if(TS.length == 1)
{
enum StringOf = (TS[0]).stringof;
}
else
{
enum StringOf = (TS[0]).stringof ~ "," ~ StringOf!(TS[1..$]) ;
}
}
template ArgStringOf(TS...)
{
static if(TS.length == 0)
{
enum ArgStringOf = "";
}
else static if(TS.length == 1)
{
enum ArgStringOf = TS[0];
}
else
{
enum ArgStringOf = TS[0] ~ "," ~ ArgStringOf!(TS[1..$]);
}
}
string combine(string[] types, string[] members)
{
assert(types.length == members.length);
string combined = "";
for(int i=0; i < (types.length) ; ++i)
{
combined ~= types[i] ~ " " ~ members[i] ~ ", ";
}
if(combined != "") combined = combined[0..$-2] ; //trim end ", "
return combined;
}
template GenProperty(string M, string N, alias SN)
{
enum GenProperty = `
@property auto ref ` ~ N ~ `(` ~
combine( StringOf!(PTT!(SN)).split(","),
ArgStringOf!(PIT!(SN)).split(",") )~ `)
{ return ` ~ M ~ `(` ~ ArgStringOf!(PIT!(SN)) ~ `); }`;
//pragma(msg, GenProperty);
}
string genProperty(string mem, string name, string s_name)
{
return `static if (isCallable!(` ~ s_name ~ `.` ~ name ~ `))
{
mixin (GenProperty!( "`~ mem ~`", "`~ name ~`", `~ s_name ~`.`~ name ~`));
}
else
{
@property auto ref ` ~ name ~ `() { return ` ~ mem ~ `; };
@property ` ~ name ~ `(typeof(` ~ s_name ~ `.` ~ name ~ `) _` ~ name ~ `)
{ ` ~ mem ~ ` = _` ~ name ~ `; }
}
`;
}
string getAlias(uint id, string[] members, string s_name)
{
string output;
foreach(m ; members)
{
output ~= genProperty("this._s" ~ to!string(id) ~ "." ~ m, m, s_name);
}
return output;
}
template GenAliases(uint count, U...)
{
static if (U.length == 0)
{
enum GenAliases = "";
}
else
{
enum GenAliases = getAlias(count,
Filter!(__traits(allMembers, U[0]))[0..$-1].split(","),
U[0].stringof) ~ GenAliases!(1+count, U[1..$]);
}
}
/*
struct test2
{
int X;
int sumX(int val) { return (cast(int)(X + val)); }
}
*/
//pragma(msg, GenAliases!(0, test1!(int), test2) );
template Gen(string name, U...)
{
static assert(U.length != 0); //otherwise no use for this template
enum Gen = `struct ` ~ name ~ ` { ` ~ GenStructs!(0, U) ~
GenAliases!(0, U) ~ ` }`;
//pragma(msg, Gen);
}
//pragma(msg, Gen!("T1", test1!(int)));
//mixin (Gen!("T1", test1!(int)));
////////////////////////////////////////////////////////////
struct S_A(AT)
if( isIntegral!AT )
{
AT A;
AT sumA(AT val) { return (cast(AT)(A + val)); }
AT doubleA() { return (2*A); }
}
struct S_B(BT)
if( isFloatingPoint!BT )
{
BT B;
BT sumB(BT val) { return (cast(BT)(B + val)); }
BT doubleB() { return (2*B); }
}
struct S_C
{
string C;
string sumC(string val) { return (C ~ val); }
string doubleC() { return (C ~ C); }
}
void main()
{
import std.math : approxEqual;
mixin (Gen!("S_ABC", S_A!(int), S_B!(double), S_C));
pragma(msg, __traits(allMembers, S_ABC));
S_ABC s_abc;
s_abc.A = 10;
assert(10 == s_abc.A);
s_abc.A = s_abc.sumA(10);
assert(20 == s_abc.A);
s_abc.A = s_abc.doubleA();
assert(40 == s_abc.A);
s_abc.B = 1.11;
assert(approxEqual(1.11, s_abc.B));
s_abc.B = s_abc.sumB(2.22);
assert(approxEqual(3.33, s_abc.B));
s_abc.B = s_abc.doubleB();
assert(approxEqual(6.66, s_abc.B));
s_abc.C = "hello";
assert("hello" == s_abc.C);
s_abc.C = s_abc.sumC("world");
assert("helloworld" == s_abc.C);
s_abc.C = s_abc.doubleC();
assert("helloworldhelloworld" == s_abc.C);
}
Sample Output
tuple("_s0","_s1","_s2","A","sumA","doubleA","B","sumB","doubleB","C","sumC","doubleC")