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