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 }