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