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 }