Difference between revisions of "Combining structs"

From D Wiki
Jump to: navigation, search
(initial)
 
m (fix)
 
(6 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 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''. [http://dpaste.dzfl.pl/51f6ae0a 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==
 
<syntaxhighlight lang="D">
 
<syntaxhighlight lang="D">
import std.traits    : isIntegral, isFloatingPoint, isCallable,  
+
import std.traits    : isCallable, PIT=ParameterIdentifierTuple, PTT=ParameterTypeTuple;
                      PIT=ParameterIdentifierTuple, PTT=ParameterTypeTuple;
 
 
import std.conv      : to ;
 
import std.conv      : to ;
 
import std.algorithm : startsWith ;
 
import std.algorithm : startsWith ;
 
import std.array    : split ;
 
import std.array    : split ;
 
//import std.stdio    : writeln ;
 
  
 
template GenStructs(uint count, U...)
 
template GenStructs(uint count, U...)
Line 29: 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..$]);
 
     }
 
     }
   
 
    //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...)
 
template Filter(U...)
Line 73: Line 49:
 
     }
 
     }
 
}
 
}
 
//pragma(msg, Filter!(__traits(allMembers, test1!(int)))[0..$-1].split(",") );
 
  
 
template StringOf(TS...)
 
template StringOf(TS...)
Line 124: Line 98:
 
}
 
}
  
template GenProperty(string M, string N, alias SN)
+
template GenFunction(string M, string N, alias SN)
 
{
 
{
     enum GenProperty = `
+
     enum GenFunction = `
                         @property auto ref ` ~ N ~ `(` ~  
+
                         auto ref ` ~ N ~ `(` ~  
 
                         combine( StringOf!(PTT!(SN)).split(","),
 
                         combine( StringOf!(PTT!(SN)).split(","),
 
                                 ArgStringOf!(PIT!(SN)).split(",") )~ `)
 
                                 ArgStringOf!(PIT!(SN)).split(",") )~ `)
 
                         { return ` ~ M ~ `(` ~ ArgStringOf!(PIT!(SN)) ~ `); }`;
 
                         { return ` ~ M ~ `(` ~ ArgStringOf!(PIT!(SN)) ~ `); }`;
 
    //pragma(msg, GenProperty);
 
 
}
 
}
  
 
string genProperty(string mem, string name, string s_name)
 
string genProperty(string mem, string name, string s_name)
 
{
 
{
     return `static if (isCallable!(` ~ s_name ~ `.` ~ name ~ `))
+
     return `static if ( !__traits(compiles, ` ~ name ~ `) )
            {
+
            {    static if (isCallable!(` ~ s_name ~ `.` ~ name ~ `))
                mixin (GenProperty!( "`~ mem ~`", "`~ name ~`", `~ 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
 
             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 genAlias(uint id, string[] members, string s_name)
 
{
 
{
 
     string output;
 
     string output;
Line 170: Line 147:
 
     else
 
     else
 
     {
 
     {
         enum GenAliases = getAlias(count,  
+
         enum GenAliases = genAlias(count,  
 
                                   Filter!(__traits(allMembers, U[0]))[0..$-1].split(","),
 
                                   Filter!(__traits(allMembers, U[0]))[0..$-1].split(","),
 
                                   U[0].stringof) ~ GenAliases!(1+count, U[1..$]);
 
                                   U[0].stringof) ~ GenAliases!(1+count, U[1..$]);
Line 176: Line 153:
 
}
 
}
 
                                                      
 
                                                      
/*
 
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...)
 
template Gen(string name, U...)
 
{
 
{
     static assert(U.length != 0); //otherwise no use for this template
+
     static assert(U.length != 0);
 
      
 
      
 
     enum Gen = `struct ` ~ name ~ ` { ` ~  GenStructs!(0, U) ~  
 
     enum Gen = `struct ` ~ name ~ ` { ` ~  GenStructs!(0, U) ~  
 
                                           GenAliases!(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); }
 
}
 
               
 
  
 +
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()  
 
void main()  
{  
+
{    
     import std.math : approxEqual;
+
     import std.stdio: writeln;
 
      
 
      
     mixin (Gen!("S_ABC", S_A!(int), S_B!(double), S_C));
+
     mixin (Gen!("S_AB", S_A, S_B));
     pragma(msg, __traits(allMembers, S_ABC));
+
     pragma(msg, __traits(allMembers, S_AB));
 
      
 
      
     S_ABC s_abc;
+
     S_AB s_ab;
+
     s_ab.x = 10;
     s_abc.A = 10;
+
     s_ab.func();
     assert(10 == s_abc.A);
+
     assert(s_ab.x == 20);
   
 
    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);
 
 
 
 
}
 
}
  
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Sample 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")