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