1 /// 2 module rxd.meta2.type; 3 4 import std.traits : isInstanceOf; 5 6 /// 7 enum isRawType(T...) = T.length == 1 && 8 is(T[0]) && !is(T[0] : Type!U, U); 9 10 /// 11 enum isType(T...) = T.length == 1 && is(T[0] : Type!U, U); 12 13 /// 14 enum isAliasTuple(T) = isInstanceOf!(AliasTuple, T); 15 16 /// 17 auto aliasTuple(Types...)() 18 { 19 return AliasTuple!Types(); 20 } 21 22 template TypeSeq(T...) 23 { 24 import std.meta : AliasSeq; 25 static if (T.length == 0) 26 alias TypeSeq = AliasSeq!(); 27 28 else 29 alias TypeSeq = AliasSeq!(Type!(T[0]), TypeSeq!(T[1 .. $])); 30 } 31 32 /// 33 auto val(T...)() if (T.length == 1) 34 { 35 return Val!(T[0]).init; 36 } 37 38 /// 39 struct Val(T...) if (T.length == 1) 40 { 41 static assert (!is(T[0]), 42 "Expected value, instead of type: " ~ T[0].stringof ~ "!"); 43 44 /// 45 alias Type = typeof(T[0]); 46 47 /// 48 enum typeOf = type!Type; 49 50 /// 51 enum Type val = T[0]; 52 53 alias val this; 54 } 55 56 /// 57 struct ValTuple(vals...) 58 { 59 enum expand = vals; 60 } 61 62 /// 63 auto type(T)() 64 { 65 return Type!T.init; 66 } 67 68 /// ditto 69 alias Type(T : Type!U, U) = T; 70 71 /// 72 unittest 73 { 74 static assert (is(Type!(Type!int) == Type!int)); 75 } 76 77 /// ditto 78 struct Type(T) 79 { 80 /// 81 alias typeOf = T; 82 83 const pure nothrow @safe @nogc: 84 85 /// 86 bool opEquals(U)(Type!U other) 87 { return is(T == U); } 88 89 /// 90 auto opSlice()() 91 { return Type!(T[]).init; } 92 93 /// 94 auto opIndex(S)(S sizeVal) if (is(S : Val!size, size_t size)) 95 { return Type!(T[sizeVal.val]).init; } 96 97 /// 98 auto opIndex(U)(Type!U keyType) 99 { return Type!(T[keyType.typeOf]).init; } 100 101 auto opBinary(string op, U)(Type!U other) 102 if (op == "+" || op == "*") 103 { 104 import std.typecons : Tuple; 105 import std.variant : Algebraic; 106 107 static if (op == "+") 108 return Type!(Algebraic!(T, U)).init; 109 110 else static if (op == "*") 111 return Type!(Tuple!(T, U)).init; 112 113 } 114 115 static if (is(typeOf == int)) 116 /// 117 unittest 118 { 119 import std.typecons : Tuple; 120 import std.variant : Algebraic; 121 122 enum t1 = type!int; 123 enum t2 = type!double; 124 125 enum t3 = t1 + t2; 126 static assert (t3 == type!(Algebraic!(int, double))); 127 128 enum t4 = t1 * t2; 129 static assert (t4 == type!(Tuple!(int, double))); 130 131 enum t5 = (t1 + t2) * t1 + t4; 132 static assert (t5 == 133 type!( 134 Algebraic!( 135 Tuple!( 136 Algebraic!(int, double), 137 int 138 ), 139 Tuple!(int, double) 140 ) 141 ) 142 ); 143 } 144 145 /// 146 auto unqualOf() 147 { 148 import std.traits : Unqual; 149 return Type!(Unqual!T).init; 150 } 151 152 /// 153 auto constOf()() 154 { return Type!(const(T)).init; } 155 156 /// 157 auto immutableOf()() 158 { return Type!(immutable(T)).init; } 159 160 /// 161 auto sharedOf()() 162 { return Type!(shared(T)).init; } 163 164 /// Convience function that forwards to std.traits. 165 auto opDispatch(string traitName)() 166 { 167 import std.meta : AliasSeq, allSatisfy; 168 import std.traits; 169 170 alias res = AliasSeq!(mixin(traitName ~ "!(this.typeOf)")); 171 172 static if (res.length == 1 && is(typeof(res[0]))) 173 // Return values directly 174 return res[0]; 175 176 else static if (res.length == 1 && is(res[0])) 177 // Wrap types in Type(T) 178 return type!res; 179 180 else static if (allSatisfy!(isRawType, res)) 181 // Return AliasTuple 182 return aliasTuple!(TypeSeq!res); 183 184 else 185 // Wrap value tuples in ValTuple 186 return ValTuple!res.init; 187 } 188 189 static if (is(T == int)) 190 /// 191 unittest 192 { 193 enum t1 = type!int; 194 static assert (t1.opDispatch!"isIntegral"); 195 196 static assert ( type!float.isFloatingPoint); 197 static assert (!type!float.isBoolean); 198 199 enum t2 = type!string[type!int[]]; 200 static assert (t2 == type!(string[int[]])); 201 static assert ( t2.isAssociativeArray); 202 static assert (!t2.isArray); 203 204 enum t3 = type!(byte*); 205 static assert (t3.PointerTarget == type!byte); 206 207 enum Names : string { n1 = "asd", n2 = "bfg" } 208 enum t4 = type!Names; 209 static assert (t4.OriginalType == type!string); 210 enum members = t4.EnumMembers; 211 static assert ([t4.EnumMembers.expand] == [Names.n1, Names.n2]); 212 213 class C0 {} 214 class C1 : C0 {} 215 class C2 : C1 {} 216 class C3 : C2 {} 217 218 enum t5 = type!C3; 219 static assert (t5.BaseClassesTuple == 220 aliasTuple!(Type!C2, Type!C1, Type!C0, Type!Object)); 221 222 interface I0 { } 223 interface I1 { } 224 interface I2 { } 225 interface I12 : I0, I1, I2 { } 226 interface I12_12 : I1, I2, I12 {} 227 228 enum t6 = type!I12_12; 229 static assert (t6.InterfacesTuple == 230 aliasTuple!(Type!I1, Type!I2, Type!I12, Type!I0)); 231 } 232 233 /// 234 string toString() 235 { 236 return typeof(this).stringof; 237 } 238 } 239 240 unittest 241 { 242 enum t1 = type!int; 243 enum t2 = t1.constOf; 244 enum t3 = t2.sharedOf; 245 246 static assert (is(typeof(t1) == Type!int)); 247 static assert (is(t1.typeOf == int)); 248 static assert (is(typeof(t2) == Type!(const(int)))); 249 static assert (is(t3.typeOf == shared(const(int)))); 250 static assert (t3.unqualOf == t1); 251 252 // operator opSlice() ( [] ) yields an array 253 enum t4 = t1[]; 254 static assert (is(t4.typeOf == int[])); 255 256 // can easily chain methods to generate complex types 257 enum t5 = t1.immutableOf[].sharedOf; 258 259 // operator opEquals(Type!U) ( == ) can be use check if two types 260 // are the same 261 static assert (t5 == type!(shared(immutable(int)[]))); 262 263 // operator opIndex(Type!U) ( e.g. [ Type!string ] ) can be used to 264 // generate associative arrays 265 enum t6 = t1[type!string]; 266 static assert (t6.isAssociativeArray); 267 static assert (t6 == type!(int[string])); 268 269 // operator opIndex(Val!size) ( e.g. [ Val!5 ] ) can be used to 270 // generate static arrays 271 enum t7 = t1[val!4]; 272 static assert (t7.isStaticArray); 273 static assert (t7 == type!(int[4])); 274 } 275 276 /// 277 struct AliasTuple(Types...) 278 { 279 //Types expand; 280 //alias expand this; 281 alias typeOf = typeof(this); 282 alias typeSeq = SeqFromTuple!(typeOf); 283 alias typeTypeSeq = Types; 284 285 enum empty = Types.length == 0; 286 287 static if (empty) 288 { 289 // Special case allowing 290 // ElementType!(typeof(this)) == TypeTuple!() 291 // in generic code. 292 293 auto front()() { return this; } 294 auto back()() { return this; } 295 auto dropOne()() { static assert (0); } 296 auto dropBackOne()() { static assert (0); } 297 } 298 else 299 { 300 auto front()() { return Types[0].init; } 301 auto back()() { return Types[$ - 1].init; } 302 auto dropOne()() { return AliasTuple!(Types[1 .. $]).init; } 303 auto dropBackOne()() { return AliasTuple!(Types[0 .. $ - 1]).init; } 304 } 305 306 string toString() 307 { 308 return typeOf.stringof; 309 } 310 } 311 312 template SeqFromTuple(Tuple : AliasTuple!TL, TL...) 313 { 314 import std.meta : AliasSeq; 315 316 static if (TL.length == 0) 317 alias SeqFromTuple = AliasSeq!(); 318 else 319 alias SeqFromTuple = AliasSeq!( 320 TL[0].typeOf, 321 SeqFromTuple!(Tuple.init.dropOne().typeOf) 322 ); 323 324 } 325 326 auto append(T, TT)(TT tuple, T type) 327 { 328 return AliasTuple!(tuple.typeTypeSeq, T)(); 329 }