1 /** 2 * Metaprogramming 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.meta; 15 16 public import ae.utils.meta.reference; 17 public import ae.utils.meta.x; 18 public import ae.utils.meta.proxy; 19 public import ae.utils.meta.binding_v1; 20 public import ae.utils.meta.binding; 21 22 // ************************************************************************ 23 24 import std.algorithm; 25 import std.range; 26 import std.traits; 27 import std.typetuple; 28 29 /** 30 * Same as TypeTuple, but meant to be used with values. 31 * 32 * Example: 33 * foreach (char channel; ValueTuple!('r', 'g', 'b')) 34 * { 35 * // the loop is unrolled at compile-time 36 * // "channel" is a compile-time value, and can be used in string mixins 37 * } 38 */ 39 template ValueTuple(T...) 40 { 41 alias T ValueTuple; 42 } 43 44 template RangeTupleImpl(size_t N, R...) 45 { 46 static if (N==R.length) 47 alias R RangeTupleImpl; 48 else 49 alias RangeTupleImpl!(N, ValueTuple!(R, R.length)) RangeTupleImpl; 50 } 51 52 /// Generate a tuple containing integers from 0 to N-1. 53 /// Useful for static loop unrolling. (staticIota) 54 template RangeTuple(size_t N) 55 { 56 alias RangeTupleImpl!(N, ValueTuple!()) RangeTuple; 57 } 58 59 template ArrayToTuple(alias arr, Elements...) 60 { 61 static if (arr.length) 62 alias ArrayToTuple = ArrayToTuple!(arr[1..$], ValueTuple!(Elements, arr[0])); 63 else 64 alias ArrayToTuple = Elements; 65 } 66 67 unittest 68 { 69 alias X = ArrayToTuple!"abc"; 70 static assert(X[0] == 'a' && X[2] == 'c'); 71 static assert([X] == "abc"); 72 } 73 74 /// Return something to foreach over optimally. 75 /// If A is known at compile-time, return a tuple, 76 /// so the foreach is unrolled at compile-time. 77 /// Otherwise, return A for a regular runtime foreach. 78 template CTIterate(alias A) 79 { 80 static if (is(typeof(ArrayToTuple!A))) 81 enum CTIterate = ArrayToTuple!A; 82 else 83 alias CTIterate = A; 84 } 85 86 unittest 87 { 88 foreach (c; CTIterate!"abc") {} 89 string s; 90 foreach (c; CTIterate!s) {} 91 } 92 93 /// Like std.typecons.Tuple, but a template mixin. 94 /// Unlike std.typecons.Tuple, names may not be omitted - but repeating types may be. 95 /// Example: FieldList!(ubyte, "r", "g", "b", ushort, "a"); 96 mixin template FieldList(Fields...) 97 { 98 mixin(GenFieldList!(void, Fields)); 99 } 100 101 template GenFieldList(T, Fields...) 102 { 103 static if (Fields.length == 0) 104 enum GenFieldList = ""; 105 else 106 { 107 static if (is(typeof(Fields[0]) == string)) 108 enum GenFieldList = T.stringof ~ " " ~ Fields[0] ~ ";\n" ~ GenFieldList!(T, Fields[1..$]); 109 else 110 enum GenFieldList = GenFieldList!(Fields[0], Fields[1..$]); 111 } 112 } 113 114 unittest 115 { 116 struct S 117 { 118 mixin FieldList!(ubyte, "r", "g", "b", ushort, "a"); 119 } 120 S s; 121 static assert(is(typeof(s.r) == ubyte)); 122 static assert(is(typeof(s.g) == ubyte)); 123 static assert(is(typeof(s.b) == ubyte)); 124 static assert(is(typeof(s.a) == ushort)); 125 } 126 127 /// Return true if all of T's fields are the same type. 128 @property bool isHomogenous(T)() 129 { 130 foreach (i, f; T.init.tupleof) 131 if (!is(typeof(T.init.tupleof[i]) == typeof(T.init.tupleof[0]))) 132 return false; 133 return true; 134 } 135 136 template isValueOfTypeInTuple(X, T...) 137 { 138 static if (T.length==0) 139 enum bool isValueOfTypeInTuple = false; 140 else 141 static if (T.length==1) 142 enum bool isValueOfTypeInTuple = is(typeof(T[0]) : X); 143 else 144 enum bool isValueOfTypeInTuple = isValueOfTypeInTuple!(X, T[0..$/2]) || isValueOfTypeInTuple!(X, T[$/2..$]); 145 } 146 147 unittest 148 { 149 static assert( isValueOfTypeInTuple!(int, ValueTuple!("a", 42))); 150 static assert(!isValueOfTypeInTuple!(int, ValueTuple!("a", 42.42))); 151 static assert(!isValueOfTypeInTuple!(int, ValueTuple!())); 152 153 static assert(!isValueOfTypeInTuple!(int, "a", int, Object)); 154 static assert( isValueOfTypeInTuple!(int, "a", int, Object, 42)); 155 } 156 157 template findValueOfTypeInTuple(X, T...) 158 { 159 static if (T.length==0) 160 static assert(false, "Can't find value of type " ~ X.stringof ~ " in specified tuple"); 161 else 162 static if (is(typeof(T[0]) : X)) 163 enum findValueOfTypeInTuple = T[0]; 164 else 165 enum findValueOfTypeInTuple = findValueOfTypeInTuple!(X, T[1..$]); 166 } 167 168 unittest 169 { 170 static assert(findValueOfTypeInTuple!(int, ValueTuple!("a", 42))==42); 171 static assert(findValueOfTypeInTuple!(int, "a", int, Object, 42)==42); 172 } 173 174 /// One past the biggest element of the enum T. 175 /// Example: string[enumLength!E] arr; 176 template enumLength(T) 177 if (is(T==enum)) 178 { 179 enum enumLength = cast(T)(cast(size_t)T.max + 1); 180 } 181 182 deprecated alias EnumLength = enumLength; 183 184 /// A range that iterates over all members of an enum. 185 @property auto enumIota(T)() { return iota(T.init, enumLength!T); } 186 187 unittest 188 { 189 enum E { a, b, c } 190 static assert(equal(enumIota!E, [E.a, E.b, E.c])); 191 } 192 193 // ************************************************************************ 194 195 // http://d.puremagic.com/issues/show_bug.cgi?id=7805 196 static template stringofArray(Args...) 197 { 198 static string[] stringofArray() 199 { 200 string[] args; 201 foreach (i, _ ; typeof(Args)) 202 args ~= Args[i].stringof; 203 return args; 204 } 205 } 206 207 /// Returns the index of fun's parameter with the name 208 /// matching "names", or asserts if the parameter is not found. 209 /// "names" can contain multiple names separated by slashes. 210 static size_t findParameter(alias fun, string names)() 211 { 212 foreach (name; names.split("/")) 213 foreach (i, param; ParameterIdentifierTuple!fun) 214 if (param == name) 215 return i; 216 assert(false, "Function " ~ __traits(identifier, fun) ~ " doesn't have a parameter called " ~ name); 217 } 218 219 /// ditto 220 // Workaround for no "static alias" template parameters 221 static size_t findParameter()(string[] searchedNames, string soughtNames, string funName) 222 { 223 foreach (soughtName; soughtNames.split("/")) 224 { 225 auto targetIndex = searchedNames.countUntil(soughtName); 226 if (targetIndex >= 0) 227 return targetIndex; 228 } 229 assert(false, "No argument %s in %s's parameters (%s)".format(soughtNames, funName, searchedNames).idup); 230 } 231 232 /// Generates a function which passes its arguments to a struct, which is 233 /// returned. Preserves field names (as parameter names) and default values. 234 template structFun(S) 235 { 236 string gen() 237 { 238 enum identifierAt(int n) = __traits(identifier, S.tupleof[n]); 239 enum names = [staticMap!(identifierAt, RangeTuple!(S.tupleof.length))]; 240 241 return 242 "S structFun(\n" ~ 243 S.tupleof.length.iota.map!(n => 244 " typeof(S.init.tupleof[%d]) %s = S.init.tupleof[%d],\n".format(n, names[n], n) 245 ).join() ~ 246 `) { return S(` ~ names.join(", ") ~ "); }"; 247 } 248 249 mixin(gen()); 250 } 251 252 unittest 253 { 254 static struct Test 255 { 256 string a; 257 int b = 42; 258 } 259 260 Test test = structFun!Test("banana"); 261 assert(test.a is "banana"); 262 assert(test.b == 42); 263 } 264 265 // ************************************************************************ 266 267 // Using a compiler with UDA support? 268 enum HAVE_UDA = __traits(compiles, __traits(getAttributes, Object)); 269 270 static if (HAVE_UDA) 271 { 272 template hasAttribute(T, alias D) 273 { 274 enum bool hasAttribute = isValueOfTypeInTuple!(T, __traits(getAttributes, D)); 275 } 276 277 template getAttribute(T, alias D) 278 { 279 enum T getAttribute = findValueOfTypeInTuple!(T, __traits(getAttributes, D)); 280 } 281 } 282 else 283 { 284 template hasAttribute(T, alias D) 285 { 286 enum bool hasAttribute = false; 287 } 288 289 template getAttribute(T, alias D) 290 { 291 static assert(false, "This D compiler has no UDA support."); 292 } 293 } 294 295 // ************************************************************************ 296 297 import std.conv; 298 import std..string; 299 300 string mixGenerateContructorProxies(T)() 301 { 302 string s; 303 static if (__traits(hasMember, T, "__ctor")) 304 foreach (ctor; __traits(getOverloads, T, "__ctor")) 305 { 306 string[] declarationList, usageList; 307 foreach (i, param; ParameterTypeTuple!(typeof(&ctor))) 308 { 309 auto varName = "v" ~ text(i); 310 declarationList ~= param.stringof ~ " " ~ varName; 311 usageList ~= varName; 312 } 313 s ~= "this(" ~ declarationList.join(", ") ~ ") { super(" ~ usageList.join(", ") ~ "); }\n"; 314 } 315 return s; 316 } 317 318 /// Generate constructors that simply call the parent class constructors. 319 /// Based on http://forum.dlang.org/post/i3hpj0$2vc6$1@digitalmars.com 320 mixin template GenerateContructorProxies() 321 { 322 mixin(mixGenerateContructorProxies!(typeof(super))()); 323 } 324 325 unittest 326 { 327 class A 328 { 329 int i, j; 330 this() { } 331 this(int i) { this.i = i; } 332 this(int i, int j ) { this.i = i; this.j = j; } 333 } 334 335 class B : A 336 { 337 mixin GenerateContructorProxies; 338 } 339 340 A a; 341 342 a = new B(); 343 assert(a.i == 0); 344 a = new B(17); 345 assert(a.i == 17); 346 a = new B(17, 42); 347 assert(a.j == 42); 348 } 349 350 // ************************************************************************ 351 352 /// Generate a @property function which creates/returns 353 /// a thread-local singleton of a class with the given arguments. 354 355 @property T singleton(T, args...)() 356 if (is(typeof(new T(args)))) 357 { 358 static T instance; 359 if (!instance) 360 instance = new T(args); 361 return instance; 362 } 363 364 unittest 365 { 366 static class C 367 { 368 static int n = 0; 369 370 this() { n++; } 371 this(int x) { n += x; } 372 373 void fun() {} 374 } 375 376 alias singleton!C c0; 377 c0.fun(); 378 c0.fun(); 379 assert(C.n == 1); 380 381 alias singleton!(C, 5) c1; 382 c1.fun(); 383 c1.fun(); 384 assert(C.n == 6); 385 } 386 387 // ************************************************************************ 388 389 /// Were we built with -debug? 390 debug 391 enum isDebug = true; 392 else 393 enum isDebug = false; 394 395 deprecated alias IsDebug = isDebug; 396 397 /// Is a specific version on? 398 template isVersion(string versionName) 399 { 400 mixin(`version (` ~ versionName ~ `) enum isVersion = true; else enum isVersion = false;`); 401 } 402 403 // ************************************************************************ 404 405 /// Shorter synonym for std.traits.Identity. 406 /// Can be used to UFCS-chain static methods and nested functions. 407 alias I(alias A) = A; 408 409 /// Get f's ancestor which represents its "this" pointer. 410 /// Skips template and mixin ancestors until it finds a struct or class. 411 template thisOf(alias f) 412 { 413 alias p = Identity!(__traits(parent, f)); 414 static if (is(p == class) || is(p == struct) || is(p == union)) 415 alias thisOf = p; 416 else 417 alias thisOf = thisOf!p; 418 } 419 420 // ************************************************************************ 421 422 /// Return the number of bits used to store the value part, i.e. 423 /// T.sizeof*8 for integer parts and the mantissa size for 424 /// floating-point types. 425 template valueBits(T) 426 { 427 static if (is(T : ulong)) 428 enum valueBits = T.sizeof * 8; 429 else 430 static if (is(T : real)) 431 enum valueBits = T.mant_dig; 432 else 433 static assert(false, "Don't know how many value bits there are in " ~ T.stringof); 434 } 435 436 static assert(valueBits!uint == 32); 437 static assert(valueBits!double == 53); 438 439 /// Expand to a built-in numeric type of the same kind 440 /// (signed integer / unsigned integer / floating-point) 441 /// with at least the indicated number of bits of precision. 442 template ResizeNumericType(T, uint bits) 443 { 444 static if (is(T : ulong)) 445 static if (isSigned!T) 446 alias ResizeNumericType = SignedBitsType!bits; 447 else 448 alias ResizeNumericType = UnsignedBitsType!bits; 449 else 450 static if (is(T : real)) 451 { 452 static if (bits <= float.mant_dig) 453 alias ResizeNumericType = float; 454 else 455 static if (bits <= double.mant_dig) 456 alias ResizeNumericType = double; 457 else 458 static if (bits <= real.mant_dig) 459 alias ResizeNumericType = real; 460 else 461 static assert(0, "No floating-point type big enough to fit " ~ bits.stringof ~ " bits"); 462 } 463 else 464 static assert(false, "Don't know how to resize type: " ~ T.stringof); 465 } 466 467 static assert(is(ResizeNumericType!(float, double.mant_dig) == double)); 468 469 /// Expand to a built-in numeric type of the same kind 470 /// (signed integer / unsigned integer / floating-point) 471 /// with at least additionalBits more bits of precision. 472 alias ExpandNumericType(T, uint additionalBits) = 473 ResizeNumericType!(T, valueBits!T + additionalBits); 474 475 /// Unsigned integer type big enough to fit N bits of precision. 476 template UnsignedBitsType(uint bits) 477 { 478 static if (bits <= 8) 479 alias ubyte UnsignedBitsType; 480 else 481 static if (bits <= 16) 482 alias ushort UnsignedBitsType; 483 else 484 static if (bits <= 32) 485 alias uint UnsignedBitsType; 486 else 487 static if (bits <= 64) 488 alias ulong UnsignedBitsType; 489 else 490 static assert(0, "No integer type big enough to fit " ~ bits.stringof ~ " bits"); 491 } 492 493 template SignedBitsType(uint bits) 494 { 495 alias Signed!(UnsignedBitsType!bits) SignedBitsType; 496 } 497 498 /// Evaluates to array of strings with name for each field. 499 @property string[] structFields(T)() 500 if (is(T == struct) || is(T == class)) 501 { 502 import std..string : split; 503 504 string[] fields; 505 foreach (i, f; T.init.tupleof) 506 { 507 string field = T.tupleof[i].stringof; 508 field = field.split(".")[$-1]; 509 fields ~= field; 510 } 511 return fields; 512 }