Combining structs

From D Wiki
Revision as of 10:13, 22 December 2012 by R m r (talk | contribs) (small edit)
Jump to: navigation, search
Level: Novice
Cookbook Type: Recipe
Cookbook Status: Draft
Approximate reading time: 15 min
0.249 hr
D Version: D2


This recipe is essentially a workaround for the lack of availability of multiple alias this. The code shows how a template Gen is used to generate a struct S_ABC from multiple structs S_A, S_B and S_C. The template Gen generates properties and functions within S_ABC with which the fields and functions of S_A, S_B and S_C can be accessed. Run the code

Program Code

import std.traits    : isCallable, PIT=ParameterIdentifierTuple, PTT=ParameterTypeTuple;
import std.conv      : to ;
import std.algorithm : startsWith ;
import std.array     : split ;

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..$]);
    }
}

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..$]);
        }
    }
}

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 GenFunction(string M, string N, alias SN)
{
    enum GenFunction = `
                        auto ref ` ~ N ~ `(` ~ 
                        combine( StringOf!(PTT!(SN)).split(","),
                                 ArgStringOf!(PIT!(SN)).split(",") )~ `)
                        { return ` ~ M ~ `(` ~ ArgStringOf!(PIT!(SN)) ~ `); }`;
}

string genProperty(string mem, string name, string s_name)
{
    return `static if ( !__traits(compiles, ` ~ name ~ `) )
            {    static if (isCallable!(` ~ s_name ~ `.` ~ name ~ `))
                {
                    mixin (GenFunction!( "`~ mem ~`", "`~ name ~`", `~ s_name ~`.`~ name ~`));
                }
                else
                {
                    @property auto ref ` ~ name ~ `() { return ` ~ mem ~ `; };
                    @property ` ~ name ~ `(typeof(` ~ s_name ~ `.` ~ name ~ `) _` ~ name ~ `)
                    { ` ~ mem ~ ` = _` ~ name ~ `; }
                }
            }
            else
            {
            }
            `;
}

string genAlias(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 = genAlias(count, 
                                   Filter!(__traits(allMembers, U[0]))[0..$-1].split(","),
                                   U[0].stringof) ~ GenAliases!(1+count, U[1..$]);
    }
}
                                                     
template Gen(string name, U...)
{
    static assert(U.length != 0);
    
    enum Gen = `struct ` ~ name ~ ` { ` ~  GenStructs!(0, U) ~ 
                                           GenAliases!(0, U) ~ ` }`;
}


///////////////////////////////////////////////////////////////////////////////////

struct S_A { int x; int y; void func() { x = 2*x; } ; void funcA() { } ; }
struct S_B { int x; int z; void func() { x = 3*x; } ; void funcB() { } ; } 
                                                     
void main() 
{     
    import std.stdio: writeln;
    
    mixin (Gen!("S_AB", S_A, S_B));
    pragma(msg, __traits(allMembers, S_AB));
     
    S_AB s_ab;
    s_ab.x = 10;
    s_ab.func();
    assert(s_ab.x == 20);
}

Compilation Output

tuple("_s0","_s1","_s2","A","sumA","doubleA","B","sumB","doubleB","C","sumC","doubleC")