1 /** 2 * Copyright: © 2014-2015 Anton Gushcha 3 * License: Subject to the terms of the Boost 1.0 license as specified in LICENSE file 4 * Authors: Anton Gushcha <ncrashed@gmail.com> 5 * 6 * Definition of expression lists - the base of meta-programming facilities of the library. 7 */ 8 module stribog.meta.base; 9 10 /** 11 * Simple expression list wrapper. 12 * 13 * See_Also: Expression list at dlang.org documentation. 14 */ 15 template ExpressionList(T...) 16 { 17 alias ExpressionList = T; 18 19 /// Pretty printing 20 template toString() { 21 private template innerToString(U...) { 22 static if(U.length == 0) { 23 enum innerToString = ""; 24 } 25 else static if(U.length == 1) { 26 enum innerToString = staticToString!(U[0]); 27 } 28 else { 29 enum innerToString = staticToString!(U[0]) ~ ", " ~ innerToString!(U[1 .. $]); 30 } 31 } 32 33 static if(T.length == 0) { 34 enum toString = "StrictExpressionList!()"; 35 } 36 else static if(T.length == 1) { 37 enum toString = "StrictExpressionList!(" ~ staticToString!(T[0]) ~ ")"; 38 } 39 else { 40 enum toString = "StrictExpressionList!(" ~ innerToString!T ~ ")"; 41 } 42 } 43 } 44 /// Example 45 unittest 46 { 47 static assert([ExpressionList!(1, 2, 3)] == [1, 2, 3]); 48 } 49 50 /** 51 * Sometimes we don't want to auto expand expression ExpressionLists. 52 * That can be used to pass several lists into templates without 53 * breaking their boundaries. 54 */ 55 template StrictExpressionList(T...) 56 { 57 /// Explicit expand 58 alias expand = T; 59 60 /// Pretty printing 61 template toString() { 62 private template innerToString(U...) { 63 static if(U.length == 0) { 64 enum innerToString = ""; 65 } 66 else static if(U.length == 1) { 67 enum innerToString = staticToString!(U[0]); 68 } 69 else { 70 enum innerToString = staticToString!(U[0]) ~ ", " ~ innerToString!(U[1 .. $]); 71 } 72 } 73 74 static if(T.length == 0) { 75 enum toString = "StrictExpressionList!()"; 76 } 77 else static if(T.length == 1) { 78 enum toString = "StrictExpressionList!(" ~ staticToString!(T[0]) ~ ")"; 79 } 80 else { 81 enum toString = "StrictExpressionList!(" ~ innerToString!T ~ ")"; 82 } 83 } 84 } 85 /// Example 86 unittest 87 { 88 template Test(alias T1, alias T2) 89 { 90 static assert([T1.expand] == [1, 2]); 91 static assert([T2.expand] == [3, 4]); 92 enum Test = true; 93 } 94 95 static assert(Test!(StrictExpressionList!(1, 2), StrictExpressionList!(3, 4))); 96 } 97 98 /** 99 * Checks two expression lists to be equal. 100 * $(B ET1) and $(B ET2) should be wrapped to $(B StrictExpressionList). 101 * 102 * Types are checked via $(B is) operator. Values are compared via $(B ==) operator. 103 * Templates are checked via special inner template-member $(B opEquals) or via 104 * ($B stringof) conversion (if no one from pair does not define $(B opEquals)). 105 */ 106 template staticEqual(alias ET1, alias ET2) 107 { 108 alias T1 = ET1.expand; 109 alias T2 = ET2.expand; 110 111 static if(T1.length == 0 || T2.length == 0) // length isn't equal thus not equal 112 { 113 enum staticEqual = T1.length == T2.length; 114 } 115 else 116 { 117 static if(is(T1[0]) && is(T2[0])) // is types 118 { 119 enum staticEqual = is(T1[0] == T2[0]) && 120 staticEqual!(StrictExpressionList!(T1[1 .. $]), StrictExpressionList!(T2[1 .. $])); 121 } else static if(!is(T1[0]) && !is(T2[0])) // isn't types 122 { 123 static if(is(typeof(T1[0]) == void) && is(typeof(T2[0]) == void)) // check if both are templates 124 { 125 static if(__traits(compiles, T1[0].opEquals!(T2[0]))) // first has opEquals 126 enum staticEqual = T1[0].opEquals!(T2[0]) && 127 staticEqual!(StrictExpressionList!(T1[1 .. $]), StrictExpressionList!(T2[1 .. $])); 128 else static if(__traits(compiles, T2[0].opEquals!(T1[0]))) // second has opEquals 129 enum staticEqual = T2[0].opEquals!(T1[0]) && 130 staticEqual!(StrictExpressionList!(T1[1 .. $]), StrictExpressionList!(T2[1 .. $])); 131 else // compare via strings 132 enum staticEqual = T1[0].stringof == (T2[0].stringof) && 133 staticEqual!(StrictExpressionList!(T1[1 .. $]), StrictExpressionList!(T2[1 .. $])); 134 } 135 else // are values 136 { 137 enum staticEqual = T1[0] == T2[0] && 138 staticEqual!(StrictExpressionList!(T1[1 .. $]), StrictExpressionList!(T2[1 .. $])); 139 } 140 } else // different kinds (one is type, another is template or value) 141 { 142 enum staticEqual = false; 143 } 144 } 145 } 146 /// Example 147 unittest 148 { 149 // trivial cases 150 static assert(staticEqual!(StrictExpressionList!(1, 2, 3), StrictExpressionList!(1, 2, 3))); 151 static assert(staticEqual!(StrictExpressionList!(int, float, 3), StrictExpressionList!(int, float, 3))); 152 static assert(!staticEqual!(StrictExpressionList!(int, float, 4), StrictExpressionList!(int, float, 3))); 153 static assert(!staticEqual!(StrictExpressionList!(void, float, 4), StrictExpressionList!(int, float, 4))); 154 static assert(!staticEqual!(StrictExpressionList!(1, 2, 3), StrictExpressionList!(1, void, 3))); 155 static assert(!staticEqual!(StrictExpressionList!(float), StrictExpressionList!())); 156 static assert(staticEqual!(StrictExpressionList!(), StrictExpressionList!())); 157 158 // compare templates 159 template Dummy(T) { 160 } 161 static assert(staticEqual!(StrictExpressionList!(Dummy!int), StrictExpressionList!(Dummy!int))); 162 static assert(!staticEqual!(StrictExpressionList!(Dummy!int), StrictExpressionList!(Dummy!float))); 163 164 // compare templates via opEquals 165 template DummyStrange(_T, U) { 166 alias T = _T; // re-export to outside 167 template opEquals(alias S) { 168 static if(__traits(compiles, S.T)) enum opEquals = is(T == S.T); 169 else enum opEquals = false; 170 } 171 } 172 static assert(staticEqual!(StrictExpressionList!(DummyStrange!(int, float)), StrictExpressionList!(DummyStrange!(int, ubyte)))); 173 static assert(!staticEqual!(StrictExpressionList!(DummyStrange!(int, float)), StrictExpressionList!(DummyStrange!(bool, float)))); 174 } 175 176 /** 177 * Converts to string template $(B T). 178 * 179 * If $(B T) defines inner template (or naked enum) $(B toString), then the member is used, 180 * otherwise $(B T.stringof) is printed. 181 * 182 * The template is helpful while debugging. DMD generates ugly mangled names for 183 * templates after several transformations. 184 */ 185 template staticToString(T...) 186 { 187 private template convertOne(alias U) 188 { 189 static if(__traits(compiles, U.staticToStringImpl!())) { 190 enum convertOne = U.staticToStringImpl!(); 191 } 192 else static if(__traits(compiles, U.staticToStringImpl)) { // special case for naked enum 193 enum convertOne = U.staticToStringImpl; 194 } 195 else { 196 enum convertOne = U.stringof; 197 } 198 } 199 200 static if(T.length == 0) { 201 enum staticToString = ""; 202 } 203 else static if(T.length == 1) { 204 enum staticToString = convertOne!(T[0]); 205 } 206 else { 207 enum staticToString = convertOne!(T[0]) ~ ", " ~ staticToString!(T[1 .. $]); 208 } 209 } 210 /// Example 211 unittest 212 { 213 template Dummy(T) {} 214 static assert(staticToString!(Dummy!int) == "Dummy!int"); 215 216 template DummyCustom1(T) { 217 template staticToStringImpl() { 218 enum staticToStringImpl = T.stringof; 219 } 220 } 221 static assert(staticToString!(DummyCustom1!int) == "int"); 222 223 template DummyCustom2(T) { 224 enum staticToStringImpl = T.stringof; 225 } 226 static assert(staticToString!(DummyCustom2!int) == "int"); 227 }