Combining structs

From D Wiki
Revision as of 04:57, 20 December 2012 by R m r (talk | contribs) (initial)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
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 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")