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