Difference between revisions of "Combining structs"

From D Wiki
Jump to: navigation, search
m (small edit)
m (fix)
 
(2 intermediate revisions by the same user not shown)
Line 9: Line 9:
 
}}
 
}}
  
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. [http://dpaste.dzfl.pl/a0fe0e43 Run the code]
+
This recipe is essentially a workaround for the lack of availability of '''''multiple alias this''''' in the current version of the DMD compiler. The code shows how a template ''Gen'' is used to generate a struct ''S_AB'' from structs ''S_A'' and ''S_B''. The template ''Gen'' generates properties and functions within ''S_AB'' with which the fields and functions of ''S_A'' and ''S_B'' can be accessed. [http://dpaste.dzfl.pl/485db6d6 Run the code]
  
 
==Program Code==
 
==Program Code==
Line 26: Line 26:
 
     else
 
     else
 
     {
 
     {
         enum GenStructs = "\n" ~ (U[0]).stringof ~ " _s" ~   to!string(count) ~ ";" ~  
+
         enum GenStructs = "\n private " ~ (U[0]).stringof ~ " _s" ~ to!string(count) ~ ";" ~  
 
                           GenStructs!(1+count, U[1..$]);
 
                           GenStructs!(1+count, U[1..$]);
 
     }
 
     }
Line 184: Line 184:
 
==Compilation Output==
 
==Compilation Output==
 
<pre>
 
<pre>
tuple("_s0","_s1","_s2","A","sumA","doubleA","B","sumB","doubleB","C","sumC","doubleC")
+
tuple("_s0","_s1","x","y","func","funcA","z","funcB")
 
</pre>
 
</pre>

Latest revision as of 22:32, 20 January 2013

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 in the current version of the DMD compiler. The code shows how a template Gen is used to generate a struct S_AB from structs S_A and S_B. The template Gen generates properties and functions within S_AB with which the fields and functions of S_A and S_B 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 private " ~ (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","x","y","func","funcA","z","funcB")