1 module memutils.ct; 2 3 import std.traits : isPointer, isSomeString, isType; 4 import std.meta : Filter, AliasSeq; 5 public import std.typecons : tuple, Tuple; 6 7 template getName(alias sym) 8 { 9 enum getName = __traits(identifier, sym); 10 } 11 12 template getStringUDAs(alias symbol) 13 { 14 template isStringUDA(alias t) 15 { 16 static if (isType!t) 17 { 18 enum isStringUDA = isSomeString!(t); 19 } 20 else 21 enum isStringUDA = isSomeString!(typeof(t)); 22 } 23 24 alias getStringUDAs = Filter!(isStringUDA, AliasSeq!(__traits(getAttributes, symbol))); 25 } 26 27 template getMember(alias T, string name) 28 { 29 import std.meta : AliasSeq; 30 31 alias getMember = AliasSeq!(__traits(getMember, T, name))[0]; 32 } 33 34 template from(string moduleName) 35 { 36 mixin("import from = " ~ moduleName ~ ";"); 37 } 38 39 // NOTE: below is mainly stuff from phobos modified a tiny bit for betterC 40 41 template toLower(string str) 42 { 43 static if (str.length == 0) 44 enum toLower = ""; 45 else static if (str[0] < 0xAA) 46 { 47 static if (str[0] < 'A') 48 enum toLower = c ~ toLower!(str[1 .. $]); 49 else static if (str[0] <= 'Z') 50 enum toLower = str[0] + 32 ~ toLower!(str[1 .. $]); 51 else 52 enum toLower = str[0] ~ toLower!(str[1 .. $]); 53 } 54 else 55 enum toLower = str[0] ~ toLower!(str[1 .. $]); 56 } 57 58 template capitalize(string str) 59 { 60 static if (str.length == 0) 61 enum capitalize = ""; 62 else static if (str[0] < 0xAA) 63 { 64 static if (str[0] < 'a') 65 enum capitalize = str; 66 else static if (str[0] <= 'z') 67 enum capitalize = str[0] - 32 ~ str[1 .. $]; 68 else 69 enum capitalize = str; 70 } 71 else 72 enum capitalize = str; 73 } 74 75 template isSomeFunction(T...) if (T.length == 1) 76 { 77 static if (is(typeof(&T[0]) U : U*) && is(U == function) || is(typeof(&T[0]) U == delegate)) 78 { 79 // T is a (nested) function symbol. 80 enum bool isSomeFunction = true; 81 } 82 else static if (is(T[0] W) || is(typeof(T[0]) W)) 83 { 84 // T is an expression or a type. Take the type of it and examine. 85 static if (is(W F : F*) && is(F == function)) 86 enum bool isSomeFunction = true; // function pointer 87 else 88 enum bool isSomeFunction = is(W == function) || is(W == delegate); 89 } 90 else 91 enum bool isSomeFunction = false; 92 } 93 94 template isCallable(T...) if (T.length == 1) 95 { 96 static if (is(typeof(&T[0].opCall) == delegate)) // T is a object which has a member function opCall(). 97 enum bool isCallable = true; 98 else static if (is(typeof(&T[0].opCall) V : V*) && is(V == function)) // T is a type which has a static member function opCall(). 99 enum bool isCallable = true; 100 else 101 enum bool isCallable = isSomeFunction!T; 102 } 103 104 template isDelegate(T...) if (T.length == 1) 105 { 106 static if (is(typeof(&T[0]) U : U*) && is(typeof(&T[0]) U == delegate)) 107 { 108 // T is a (nested) function symbol. 109 enum bool isDelegate = true; 110 } 111 else static if (is(T[0] W) || is(typeof(T[0]) W)) 112 { 113 // T is an expression or a type. Take the type of it and examine. 114 enum bool isDelegate = is(W == delegate); 115 } 116 else 117 enum bool isDelegate = false; 118 } 119 120 template isFunctionPointer(T...) if (T.length == 1) 121 { 122 static if (is(T[0] U) || is(typeof(T[0]) U)) 123 { 124 static if (is(U F : F*) && is(F == function)) 125 enum bool isFunctionPointer = true; 126 else 127 enum bool isFunctionPointer = false; 128 } 129 else 130 enum bool isFunctionPointer = false; 131 } 132 133 template FunctionTypeOf(func...) if (func.length == 1 && isCallable!func) 134 { 135 static if (is(typeof(&func[0]) Fsym : Fsym*) && is(Fsym == function) || is( 136 typeof(&func[0]) Fsym == delegate)) 137 { 138 alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol 139 } 140 else static if (is(typeof(&func[0].opCall) Fobj == delegate)) 141 { 142 alias FunctionTypeOf = Fobj; // HIT: callable object 143 } 144 else static if (is(typeof(&func[0].opCall) Ftyp : Ftyp*) && is(Ftyp == function)) 145 { 146 alias FunctionTypeOf = Ftyp; // HIT: callable type 147 } 148 else static if (is(func[0] T) || is(typeof(func[0]) T)) 149 { 150 static if (is(T == function)) 151 alias FunctionTypeOf = T; // HIT: function 152 else static if (is(T Fptr : Fptr*) && is(Fptr == function)) 153 alias FunctionTypeOf = Fptr; // HIT: function pointer 154 else static if (is(T Fdlg == delegate)) 155 alias FunctionTypeOf = Fdlg; // HIT: delegate 156 else 157 static assert(0); 158 } 159 else 160 static assert(0); 161 } 162 163 template ParameterIdentifierTuple(func...) if (func.length == 1 && isCallable!func) 164 { 165 import std.meta : AliasSeq; 166 167 static if (is(FunctionTypeOf!func PT == __parameters)) 168 { 169 template Get(size_t i) 170 { 171 static if (!isFunctionPointer!func && !isDelegate!func // Unnamed parameters yield CT error. 172 && is(typeof(__traits(identifier, PT[i .. i + 1])))) 173 { 174 enum Get = __traits(identifier, PT[i .. i + 1]); 175 } 176 else 177 { 178 enum Get = ""; 179 } 180 } 181 } 182 else 183 { 184 static assert(0, func[0].stringof ~ "is not a function"); 185 186 // Define dummy entities to avoid pointless errors 187 template Get(size_t i) 188 { 189 enum Get = ""; 190 } 191 192 alias PT = AliasSeq!(); 193 } 194 195 template Impl(size_t i = 0) 196 { 197 static if (i == PT.length) 198 alias Impl = AliasSeq!(); 199 else 200 alias Impl = AliasSeq!(Get!i, Impl!(i + 1)); 201 } 202 203 alias ParameterIdentifierTuple = Impl!(); 204 } 205 206 template getNamedFields(size_t I = 0, Specs...) 207 { 208 static if (Specs[0].name.length != 0) 209 enum byName = "alias " ~ Specs[0].name ~ " = _" ~ I.stringof ~ ";"; 210 else 211 enum byName = ""; 212 enum namedField = "alias _" ~ I.stringof ~ " = Identity!(field[" ~ I.stringof ~ "]);" ~ byName; 213 static if (Specs.length == 1) 214 { 215 enum getNamedFields = namedField; 216 } 217 else 218 { 219 enum getNamedFields = namedField ~ getNamedFields!(I + 1, Specs[1 .. $]); 220 } 221 } 222 223 enum bool distinctFieldNames(names...) = __traits(compiles, { 224 static foreach (__name; names) 225 static if (is(typeof(__name) : string)) 226 mixin("enum int " ~ __name ~ " = 0;"); 227 }); 228 229 // TODO: can we import this directly 230 alias Identity(alias A) = A; 231 232 enum isTuple(T) = __traits(compiles, 233 { void f(Specs...)(Tuple!Specs tup) 234 { 235 } 236 237 f(T.init); }); 238 239 template replace(string str, dchar from, dchar to) 240 { 241 static if (str.length == 0) 242 { 243 enum replace = ""; 244 } 245 else static if (str[0] == from) 246 { 247 enum replace = to ~ replace!(str[1 .. $], from, to); 248 } 249 else 250 enum replace = str[0] ~ replace!(str[1 .. $], from, to); 251 } 252 253 template Joiner(Ts...) 254 { 255 static if (Ts.length > 0) 256 { 257 enum Joiner = Ts[0] ~ Joiner!(Ts[1 .. $]); 258 } 259 else 260 enum Joiner = ""; 261 } 262 263 version (none) : template Tuple(Specs...) if (distinctFieldNames!(Specs)) 264 { 265 import std.meta : AliasSeq; 266 import std.meta : staticMap; 267 268 // Parse (type,name) pairs (FieldSpecs) out of the specified 269 // arguments. Some fields would have name, others not. 270 template parseSpecs(Specs...) 271 { 272 static if (Specs.length == 0) 273 { 274 alias parseSpecs = AliasSeq!(); 275 } 276 else static if (is(Specs[0])) 277 { 278 static if (is(typeof(Specs[1]) : string)) 279 { 280 alias parseSpecs = 281 AliasSeq!(FieldSpec!(Specs[0 .. 2]), 282 parseSpecs!(Specs[2 .. $])); 283 } 284 else 285 { 286 alias parseSpecs = 287 AliasSeq!(FieldSpec!(Specs[0]), 288 parseSpecs!(Specs[1 .. $])); 289 } 290 } 291 else 292 { 293 static assert(0, "Attempted to instantiate Tuple with an " 294 ~ "invalid argument: " ~ Specs[0].stringof); 295 } 296 } 297 298 template FieldSpec(T, string s = "") 299 { 300 alias Type = T; 301 alias name = s; 302 } 303 304 alias fieldSpecs = parseSpecs!Specs; 305 306 // Used with staticMap. 307 alias extractType(alias spec) = spec.Type; 308 alias extractName(alias spec) = spec.name; 309 310 // Returns Specs for a subtuple this[from .. to] preserving field 311 // names if any. 312 alias sliceSpecs(size_t from, size_t to) = 313 staticMap!(expandSpec, fieldSpecs[from .. to]); 314 315 template expandSpec(alias spec) 316 { 317 static if (spec.name.length == 0) 318 { 319 alias expandSpec = AliasSeq!(spec.Type); 320 } 321 else 322 { 323 alias expandSpec = AliasSeq!(spec.Type, spec.name); 324 } 325 } 326 327 enum areCompatibleTuples(Tup1, Tup2, string op) = isTuple!Tup2 && is( 328 typeof( 329 (ref Tup1 tup1, ref Tup2 tup2) { 330 static assert(tup1.field.length == tup2.field.length); 331 static foreach (i; 0 .. Tup1.Types.length) 332 { 333 { 334 auto lhs = typeof(tup1.field[i]).init; 335 auto rhs = typeof(tup2.field[i]).init; 336 static if (op == "=") 337 lhs = rhs; 338 else 339 auto result = mixin("lhs " ~ op ~ " rhs"); 340 } 341 } 342 })); 343 344 enum areBuildCompatibleTuples(Tup1, Tup2) = isTuple!Tup2 && is(typeof( 345 { 346 static assert(Tup1.Types.length == Tup2.Types.length); 347 static foreach (i; 0 .. Tup1.Types.length) 348 static assert(isBuildable!(Tup1.Types[i], Tup2.Types[i])); 349 })); 350 351 /+ Returns `true` iff a `T` can be initialized from a `U`. +/ 352 enum isBuildable(T, U) = is(typeof( 353 { U u = U.init; T t = u; })); 354 /+ Helper for partial instantiation +/ 355 template isBuildableFrom(U) 356 { 357 enum isBuildableFrom(T) = isBuildable!(T, U); 358 } 359 360 struct Tuple 361 { 362 /** 363 * The types of the `Tuple`'s components. 364 */ 365 alias Types = staticMap!(extractType, fieldSpecs); 366 367 private alias _Fields = Specs; 368 369 /** 370 * The names of the `Tuple`'s components. Unnamed fields have empty names. 371 */ 372 alias fieldNames = staticMap!(extractName, fieldSpecs); 373 374 /** 375 * Use `t.expand` for a `Tuple` `t` to expand it into its 376 * components. The result of `expand` acts as if the `Tuple`'s components 377 * were listed as a list of values. (Ordinarily, a `Tuple` acts as a 378 * single value.) 379 */ 380 Types expand; 381 mixin(getNamedFields!(0, fieldSpecs)); 382 383 static if (is(Specs)) 384 { 385 // This is mostly to make t[n] work. 386 alias expand this; 387 } 388 else 389 { 390 @property 391 ref inout(Tuple!Types) _Tuple_super() inout @trusted 392 { 393 static foreach (i; 0 .. Types.length) // Rely on the field layout 394 { 395 static assert(typeof(return).init.tupleof[i].offsetof == 396 expand[i].offsetof); 397 } 398 return *cast(typeof(return)*)&(field[0]); 399 } 400 // This is mostly to make t[n] work. 401 alias _Tuple_super this; 402 } 403 404 // backwards compatibility 405 alias field = expand; 406 407 /** 408 * Constructor taking one value for each field. 409 * 410 * Params: 411 * values = A list of values that are either the same 412 * types as those given by the `Types` field 413 * of this `Tuple`, or can implicitly convert 414 * to those types. They must be in the same 415 * order as they appear in `Types`. 416 */ 417 static if (Types.length > 0) 418 { 419 this(Types values) 420 { 421 field[] = values[]; 422 } 423 } 424 425 /** 426 * Constructor taking a compatible array. 427 * 428 * Params: 429 * values = A compatible static array to build the `Tuple` from. 430 * Array slices are not supported. 431 */ 432 this(U, size_t n)(U[n] values) 433 if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types)) 434 { 435 static foreach (i; 0 .. Types.length) 436 { 437 field[i] = values[i]; 438 } 439 } 440 441 /** 442 * Constructor taking a compatible `Tuple`. Two `Tuple`s are compatible 443 * $(B iff) they are both of the same length, and, for each type `T` on the 444 * left-hand side, the corresponding type `U` on the right-hand side can 445 * implicitly convert to `T`. 446 * 447 * Params: 448 * another = A compatible `Tuple` to build from. Its type must be 449 * compatible with the target `Tuple`'s type. 450 */ 451 this(U)(U another) if (areBuildCompatibleTuples!(typeof(this), U)) 452 { 453 field[] = another.field[]; 454 } 455 456 /** 457 * Comparison for equality. Two `Tuple`s are considered equal 458 * $(B iff) they fulfill the following criteria: 459 * 460 * $(UL 461 * $(LI Each `Tuple` is the same length.) 462 * $(LI For each type `T` on the left-hand side and each type 463 * `U` on the right-hand side, values of type `T` can be 464 * compared with values of type `U`.) 465 * $(LI For each value `v1` on the left-hand side and each value 466 * `v2` on the right-hand side, the expression `v1 == v2` is 467 * true.)) 468 * 469 * Params: 470 * rhs = The `Tuple` to compare against. It must meeting the criteria 471 * for comparison between `Tuple`s. 472 * 473 * Returns: 474 * true if both `Tuple`s are equal, otherwise false. 475 */ 476 bool opEquals(R)(R rhs) if (areCompatibleTuples!(typeof(this), R, "==")) 477 { 478 return field[] == rhs.field[]; 479 } 480 481 /// ditto 482 bool opEquals(R)(R rhs) const 483 if (areCompatibleTuples!(typeof(this), R, "==")) 484 { 485 return field[] == rhs.field[]; 486 } 487 488 /// ditto 489 bool opEquals(R...)(auto ref R rhs) 490 if (R.length > 1 && areCompatibleTuples!(typeof(this), Tuple!R, "==")) 491 { 492 static foreach (i; 0 .. Types.length) 493 if (field[i] != rhs[i]) 494 return false; 495 496 return true; 497 } 498 499 /** 500 * Comparison for ordering. 501 * 502 * Params: 503 * rhs = The `Tuple` to compare against. It must meet the criteria 504 * for comparison between `Tuple`s. 505 * 506 * Returns: 507 * For any values `v1` on the right-hand side and `v2` on the 508 * left-hand side: 509 * 510 * $(UL 511 * $(LI A negative integer if the expression `v1 < v2` is true.) 512 * $(LI A positive integer if the expression `v1 > v2` is true.) 513 * $(LI 0 if the expression `v1 == v2` is true.)) 514 */ 515 int opCmp(R)(R rhs) if (areCompatibleTuples!(typeof(this), R, "<")) 516 { 517 static foreach (i; 0 .. Types.length) 518 { 519 if (field[i] != rhs.field[i]) 520 { 521 return field[i] < rhs.field[i] ? -1 : 1; 522 } 523 } 524 return 0; 525 } 526 527 /// ditto 528 int opCmp(R)(R rhs) const 529 if (areCompatibleTuples!(typeof(this), R, "<")) 530 { 531 static foreach (i; 0 .. Types.length) 532 { 533 if (field[i] != rhs.field[i]) 534 { 535 return field[i] < rhs.field[i] ? -1 : 1; 536 } 537 } 538 return 0; 539 } 540 541 /** 542 Concatenate Tuples. 543 Tuple concatenation is only allowed if all named fields are distinct (no named field of this tuple occurs in `t` 544 and no named field of `t` occurs in this tuple). 545 Params: 546 t = The `Tuple` to concatenate with 547 Returns: A concatenation of this tuple and `t` 548 */ 549 auto opBinary(string op, T)(auto ref T t) if (op == "~") 550 { 551 static if (isTuple!T) 552 { 553 static assert(distinctFieldNames!(_Fields, T._Fields), 554 "Cannot concatenate tuples with duplicate fields: " ~ fieldNames.stringof ~ 555 " - " ~ T.fieldNames.stringof); 556 return Tuple!(_Fields, T._Fields)(expand, t.expand); 557 } 558 else 559 { 560 return Tuple!(_Fields, T)(expand, t); 561 } 562 } 563 564 /// ditto 565 auto opBinaryRight(string op, T)(auto ref T t) if (op == "~") 566 { 567 static if (isTuple!T) 568 { 569 static assert(distinctFieldNames!(_Fields, T._Fields), 570 "Cannot concatenate tuples with duplicate fields: " ~ T.stringof ~ 571 " - " ~ fieldNames.fieldNames.stringof); 572 return Tuple!(T._Fields, _Fields)(t.expand, expand); 573 } 574 else 575 { 576 return Tuple!(T, _Fields)(t, expand); 577 } 578 } 579 580 /** 581 * Assignment from another `Tuple`. 582 * 583 * Params: 584 * rhs = The source `Tuple` to assign from. Each element of the 585 * source `Tuple` must be implicitly assignable to each 586 * respective element of the target `Tuple`. 587 */ 588 ref Tuple opAssign(R)(auto ref R rhs) 589 if (areCompatibleTuples!(typeof(this), R, "=")) 590 { 591 592 static if (is(R : Tuple!Types) && !__traits(isRef, rhs)) 593 { 594 field[] = rhs.field[]; 595 } 596 else 597 { 598 // Do not swap; opAssign should be called on the fields. 599 field[] = rhs.field[]; 600 } 601 return this; 602 } 603 604 /** 605 * Renames the elements of a $(LREF Tuple). 606 * 607 * `rename` uses the passed `names` and returns a new 608 * $(LREF Tuple) using these names, with the content 609 * unchanged. 610 * If fewer names are passed than there are members 611 * of the $(LREF Tuple) then those trailing members are unchanged. 612 * An empty string will remove the name for that member. 613 * It is an compile-time error to pass more names than 614 * there are members of the $(LREF Tuple). 615 */ 616 ref rename(names...)() return 617 if (names.length == 0 || allSatisfy!(isSomeString, typeof(names))) 618 { 619 import std.algorithm.comparison : equal; 620 621 // to circumvent bug 16418 622 static if (names.length == 0 || equal([names], [fieldNames])) 623 return this; 624 else 625 { 626 enum nT = Types.length; 627 enum nN = names.length; 628 static assert(nN <= nT, "Cannot have more names than tuple members"); 629 alias allNames = AliasSeq!(names, fieldNames[nN .. $]); 630 631 template GetItem(size_t idx) 632 { 633 import std.array : empty; 634 635 static if (idx < nT) 636 alias GetItem = Alias!(Types[idx]); 637 else static if (allNames[idx - nT].length == 0) 638 alias GetItem = AliasSeq!(); 639 else 640 alias GetItem = Alias!(allNames[idx - nT]); 641 } 642 643 import std.range : roundRobin, iota; 644 645 alias NewTupleT = Tuple!(staticMap!(GetItem, aliasSeqOf!( 646 roundRobin(iota(nT), iota(nT, 2 * nT))))); 647 return *(() @trusted => cast(NewTupleT*)&this)(); 648 } 649 } 650 651 /** 652 * Overload of $(LREF _rename) that takes an associative array 653 * `translate` as a template parameter, where the keys are 654 * either the names or indices of the members to be changed 655 * and the new names are the corresponding values. 656 * Every key in `translate` must be the name of a member of the 657 * $(LREF tuple). 658 * The same rules for empty strings apply as for the variadic 659 * template overload of $(LREF _rename). 660 */ 661 ref rename(alias translate)() 662 if (is(typeof(translate) : V[K], V, K) && isSomeString!V && 663 (isSomeString!K || is(K : size_t))) 664 { 665 import std.range : ElementType; 666 667 static if (isSomeString!(ElementType!(typeof(translate.keys)))) 668 { 669 { 670 import std.conv : to; 671 import std.algorithm.iteration : filter; 672 import std.algorithm.searching : canFind; 673 674 enum notFound = translate.keys 675 .filter!(k => fieldNames.canFind(k) == -1); 676 static assert(notFound.empty, "Cannot find members " 677 ~ notFound.to!string ~ " in type " 678 ~ typeof( 679 this).stringof); 680 } 681 return this.rename!(aliasSeqOf!( 682 { 683 import std.array : empty; 684 685 auto names = [fieldNames]; 686 foreach (ref n; names) 687 if (!n.empty) 688 if (auto p = n in translate) 689 n = *p; 690 return names; 691 }())); 692 } 693 else 694 { 695 { 696 import std.algorithm.iteration : filter; 697 import std.conv : to; 698 699 enum invalid = translate.keys.filter!(k => k < 0 || k >= this.length); 700 static assert(invalid.empty, "Indices " ~ invalid.to!string 701 ~ " are out of bounds for tuple with length " 702 ~ this.length.to!string); 703 } 704 return this.rename!(aliasSeqOf!( 705 { 706 auto names = [fieldNames]; 707 foreach (k, v; translate) 708 names[k] = v; 709 return names; 710 }())); 711 } 712 } 713 714 /** 715 * Takes a slice by-reference of this `Tuple`. 716 * 717 * Params: 718 * from = A `size_t` designating the starting position of the slice. 719 * to = A `size_t` designating the ending position (exclusive) of the slice. 720 * 721 * Returns: 722 * A new `Tuple` that is a slice from `[from, to$(RPAREN)` of the original. 723 * It has the same types and values as the range `[from, to$(RPAREN)` in 724 * the original. 725 */ 726 @property 727 ref inout(Tuple!(sliceSpecs!(from, to))) slice(size_t from, size_t to)() inout @trusted 728 if (from <= to && to <= Types.length) 729 { 730 static assert( 731 (typeof(this).alignof % typeof(return).alignof == 0) && 732 (expand[from].offsetof % typeof(return).alignof == 0), 733 "Slicing by reference is impossible because of an alignment mistmatch. (See Phobos issue #15645.)"); 734 735 return *cast(typeof(return)*)&(field[from]); 736 } 737 738 /** 739 Creates a hash of this `Tuple`. 740 Returns: 741 A `size_t` representing the hash of this `Tuple`. 742 */ 743 // size_t toHash() const nothrow @safe 744 // { 745 // size_t h = 0; 746 // static foreach (i, T; Types) 747 // {{ 748 // const k = typeid(T).getHash((() @trusted => cast(const void*) &field[i])()); 749 // static if (i == 0) 750 // h = k; 751 // else 752 // // As in boost::hash_combine 753 // // https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine 754 // h ^= k + 0x9e3779b9 + (h << 6) + (h >>> 2); 755 // }} 756 // return h; 757 // } 758 759 /** 760 * Converts to string. 761 * 762 * Returns: 763 * The string representation of this `Tuple`. 764 */ 765 string toString()() const 766 { 767 import std.array : appender; 768 769 auto app = appender!string(); 770 this.toString((const(char)[] chunk) => app ~= chunk); 771 return app.data; 772 } 773 774 import std.format : FormatSpec; 775 776 /** 777 * Formats `Tuple` with either `%s`, `%(inner%)` or `%(inner%|sep%)`. 778 * 779 * $(TABLE2 Formats supported by Tuple, 780 * $(THEAD Format, Description) 781 * $(TROW $(P `%s`), $(P Format like `Tuple!(types)(elements formatted with %s each)`.)) 782 * $(TROW $(P `%(inner%)`), $(P The format `inner` is applied the expanded `Tuple`$(COMMA) so 783 * it may contain as many formats as the `Tuple` has fields.)) 784 * $(TROW $(P `%(inner%|sep%)`), $(P The format `inner` is one format$(COMMA) that is applied 785 * on all fields of the `Tuple`. The inner format must be compatible to all 786 * of them.))) 787 * 788 * Params: 789 * sink = A `char` accepting delegate 790 * fmt = A $(REF FormatSpec, std,format) 791 */ 792 void toString(DG)(scope DG sink) const 793 { 794 auto f = FormatSpec!char(); 795 toString(sink, f); 796 } 797 798 /// ditto 799 void toString(DG, Char)(scope DG sink, const ref FormatSpec!Char fmt) const 800 { 801 import std.format : formatElement, formattedWrite, FormatException; 802 803 if (fmt.nested) 804 { 805 if (fmt.sep) 806 { 807 foreach (i, Type; Types) 808 { 809 static if (i > 0) 810 { 811 sink(fmt.sep); 812 } 813 // TODO: Change this once formattedWrite() works for shared objects. 814 static if (is(Type == class) && is(Type == shared)) 815 { 816 sink(Type.stringof); 817 } 818 else 819 { 820 formattedWrite(sink, fmt.nested, this.field[i]); 821 } 822 } 823 } 824 else 825 { 826 formattedWrite(sink, fmt.nested, staticMap!(sharedToString, this.expand)); 827 } 828 } 829 else if (fmt.spec == 's') 830 { 831 enum header = Unqual!(typeof(this)).stringof ~ "(", 832 footer = ")", 833 separator = ", "; 834 sink(header); 835 foreach (i, Type; Types) 836 { 837 static if (i > 0) 838 { 839 sink(separator); 840 } 841 // TODO: Change this once formatElement() works for shared objects. 842 static if (is(Type == class) && is(Type == shared)) 843 { 844 sink(Type.stringof); 845 } 846 else 847 { 848 FormatSpec!Char f; 849 formatElement(sink, field[i], f); 850 } 851 } 852 sink(footer); 853 } 854 else 855 { 856 throw new FormatException( 857 "Expected '%s' or '%(...%)' or '%(...%|...%)' format specifier for type '" ~ 858 Unqual!(typeof(this)) 859 .stringof ~ "', not '%" ~ fmt.spec ~ "'."); 860 } 861 } 862 } 863 } 864 865 template tuple(Names...) 866 { 867 import std.meta : AliasSeq; 868 869 /** 870 Params: 871 args = Values to initialize the `Tuple` with. The `Tuple`'s type will 872 be inferred from the types of the values given. 873 Returns: 874 A new `Tuple` with its type inferred from the arguments given. 875 */ 876 auto tuple(Args...)(Args args) 877 { 878 static if (Names.length == 0) 879 { 880 // No specified names, just infer types from Args... 881 return Tuple!Args(args); 882 } 883 else static if (!is(typeof(Names[0]) : string)) 884 { 885 // Names[0] isn't a string, must be explicit types. 886 return Tuple!Names(args); 887 } 888 else 889 { 890 // Names[0] is a string, so must be specifying names. 891 static assert(Names.length == Args.length, 892 "Insufficient number of names given."); 893 894 // Interleave(a, b).and(c, d) == (a, c, b, d) 895 // This is to get the interleaving of types and names for Tuple 896 // e.g. Tuple!(int, "x", string, "y") 897 template Interleave(A...) 898 { 899 template and(B...) if (B.length == 1) 900 { 901 alias and = AliasSeq!(A[0], B[0]); 902 } 903 904 template and(B...) if (B.length != 1) 905 { 906 alias and = AliasSeq!(A[0], B[0], 907 Interleave!(A[1 .. $]).and!(B[1 .. $])); 908 } 909 } 910 911 return Tuple!(Interleave!(Args).and!(Names))(args); 912 } 913 } 914 }