1 #!/usr/bin/env rdmd-dev-module
2 
3 /** Various extensions to std.traits.
4     Copyright: Per Nordlöw 2014-.
5     License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
6     Authors: $(WEB Per Nordlöw)
7     See also: http://forum.dlang.org/thread/jbyixfbuefvdlttnyclu@forum.dlang.org#post-mailman.2199.1353742037.5162.digitalmars-d-learn:40puremagic.com
8 */
9 module traits_ex;
10 
11 import std.traits: isArray, ParameterTypeTuple, isStaticArray, isDynamicArray, isSomeChar, isSomeString, isExpressions, isIntegral, isSigned, isUnsigned;
12 import std.range: ElementType, isForwardRange, isRandomAccessRange, isInputRange, isBidirectionalRange, isOutputRange, isIterable;
13 import std.typecons : Tuple;
14 
15 /** Returns: true iff $(D ptr) is handled by the garbage collector (GC). */
16 bool isGCPointer(void* ptr)
17 {
18     import core.memory;
19     return !!GC.addrOf(ptr);
20 }
21 alias inGC = isGCPointer;
22 alias isGCed = isGCPointer;
23 
24 /** Returns: true iff all values $(D V) are the same.
25     See also: http://forum.dlang.org/post/iflpslqgrixdjwrlqqvn@forum.dlang.org
26     See also: http://forum.dlang.org/post/mheumktihihfsxxxapff@forum.dlang.org
27 */
28 template allSame(V ...)
29     if (isExpressions!V)
30 {
31     static if (V.length <= 1)
32         enum allSame = true;
33     else static if (V.length & 1)
34         enum allSame = (V[$ - 1] == V[0] &&
35                         V[0 .. $/2] == V[$/2 .. $-1] &&
36                         allSame!(V[0 .. $/2]));
37     else
38         enum allSame = (V[0 .. $/2] == V[$/2 .. $] &&
39                         allSame!(V[0 .. $/2]));
40 }
41 
42 unittest
43 {
44     static assert( allSame!());
45     static assert( allSame!(42));
46     static assert( allSame!(42, 42, 42));
47     static assert(!allSame!(42, 43, 42));
48 }
49 
50 template allSameIterative(V...)
51     if (isExpressions!V)
52 {
53     bool impl_(V...)()
54     {
55         static if (V.length >= 2)
56         {
57             foreach (i, _; V[0 .. $ - 1])
58             {
59                 if (V[i] != V[i + 1])
60                 {
61                     return false;
62                 }
63             }
64             return true;
65         }
66         else
67         {
68             return true;
69         }
70     }
71     enum allSameIterative = impl_!V();
72 }
73 
74 unittest
75 {
76     static assert( allSameIterative!());
77     static assert( allSameIterative!(42));
78     static assert( allSameIterative!(42, 42, 42));
79     static assert(!allSameIterative!(42, 43, 42));
80 }
81 
82 template allSameRecursive(V...)
83     if (isExpressions!(V))
84 {
85     static if (V.length <= 1)
86         enum allSameRecursive = true;
87     else
88         enum allSameRecursive = V[0] == V[1] && allSameRecursive!(V[1..$]);
89 }
90 
91 unittest
92 {
93     static assert(allSameRecursive!());
94     static assert(allSameRecursive!(42));
95     static assert(!allSameRecursive!(41, 42));
96     static assert(allSameRecursive!(42, 42, 42));
97 }
98 
99 /** Returns: true iff all types $(D T) are the same. */
100 template allSameType(T...)
101 {
102     static if (T.length <= 1)
103         enum allSameType = true;
104     else
105         enum allSameType = is(T[0] == T[1]) && allSameType!(T[1..$]);
106 }
107 
108 enum allSameType1(T...) = !T.length || (is(T[0] == T[T.length > 1]) && allSameType1!(T[1 .. $]));
109 
110 unittest
111 {
112     static assert(allSameType!(int, int));
113     static assert(!allSameType!(int, double));
114     static assert(!allSameType!(int, int, double));
115     static assert(allSameType!(Tuple!(int, int, int).Types, int));
116 }
117 
118 alias allTypesSame = allSameType;
119 alias isHomogeneous = allSameType;
120 enum isHomogeneousTuple(T) = isHomogeneous!(T.Types);
121 
122 unittest
123 {
124     static assert(isHomogeneousTuple!(Tuple!(int, int, int)));
125     static assert(isHomogeneousTuple!(Tuple!(float, float, float)));
126     static assert(!isHomogeneousTuple!(Tuple!(int, float, double)));
127 }
128 
129 enum isHomogeneousTupleOf(T, E) = (isHomogeneous!(T.Types) &&
130                                    is(Unqual!(T.Types[0]) == Unqual!E));
131 
132 unittest
133 {
134     import std.typecons : Tuple;
135     static assert(isHomogeneousTupleOf!(Tuple!(int, int, int), int));
136     static assert(isHomogeneousTupleOf!(Tuple!(float, float, float), float));
137     static assert(!isHomogeneousTupleOf!(Tuple!(float, float, float), int));
138 }
139 
140 /**
141    Returns $(D true) if at least one type in the $(D Tuple T)
142    is not the same as the others.
143 */
144 enum isHeterogeneous(T) = !isHomogeneous!T;
145 
146 import std.typecons : isTuple;
147 
148 /**
149    Returns $(D true) if all types in the $(D Tuple T) are the same.
150    TODO: Remove when this is merged: https://github.com/D-Programming-Language/phobos/pull/3395
151    See also: https://github.com/D-Programming-Language/phobos/pull/1672/files
152 */
153 template allSameTypesInTuple(T)
154     if (isTuple!T)
155 {
156     alias types = T.Types;
157     static if (types.length > 0)
158     {
159         template isSameTypeAsHead(U)
160         {
161             enum isSameTypeAsHead = is(U == types[0]);
162         }
163         import std.meta : allSatisfy;
164         enum allSameTypesInTuple = allSatisfy!(isSameTypeAsHead, types);
165     }
166     else
167         enum allSameTypesInTuple = true;
168 }
169 
170 @safe pure nothrow unittest
171 {
172     import std.typecons : Tuple;
173     alias HOTUP = Tuple!(int, int, int);
174     static assert(allSameTypesInTuple!HOTUP);
175 
176     HOTUP hotup = HOTUP(1, 2, 3);
177     static assert(allSameTypesInTuple!(typeof(hotup)));
178 
179     alias HETUP = Tuple!(string, bool, float);
180     static assert(!allSameTypesInTuple!(HETUP));
181 
182     HETUP hetup = HETUP("test", false, 2.345);
183     static assert(!allSameTypesInTuple!(typeof(hetup)));
184 
185     alias ZTUP = Tuple!();
186     static assert(allSameTypesInTuple!ZTUP);
187 
188     ZTUP ztup = ZTUP();
189     static assert(allSameTypesInTuple!(typeof(ztup)));
190 }
191 
192 /** Return Tuple $(D tup) as a Static Array.
193     See also: http://dpaste.dzfl.pl/d0059e6e6c09
194 */
195 inout (T.Types[0])[T.length] asStaticArray(T)(inout T tup)
196     if (allSameType!(T.Types))
197 {
198     return *cast(T.Types[0][T.length]*)&tup; // hackish
199 }
200 
201 pure nothrow @nogc unittest
202 {
203     import std.typecons: tuple;
204     auto tup = tuple("a", "b", "c", "d");
205     string[4] arr = ["a", "b", "c", "d"];
206     static assert(is(typeof(tup.asStaticArray()) == typeof(arr)));
207     assert(tup.asStaticArray() == arr);
208 }
209 
210 /** Return Tuple $(D tup) as a Dynamic Array.
211 */
212 auto asDynamicArray(T)(inout T tup)
213     if (allSameType!(T.Types))
214 {
215     alias E = T.Types[0];
216     E[] a = new E[T.length];
217     a.length = T.length;
218     foreach (i, e; tup)
219     {
220         a[i] = e;
221     }
222     return a;
223 }
224 
225 pure nothrow unittest
226 {
227     import std.typecons: tuple;
228     auto tup = tuple("a", "b", "c", "d");
229     string[4] arr = ["a", "b", "c", "d"];
230     assert(tup.asDynamicArray() == arr);
231 }
232 
233 /// Useful aliases for combinations of range predicates.
234 enum isIterableOf(R, E) = isIterable!R && is(ElementType!R == E);
235 enum isIterableOfUnqual(R, E) = isIterable!R && is(Unqual!(ElementType!R) == Unqual!E);
236 enum isIterableOfSomeString(R) = (isIterable!R && isSomeString!(ElementType!R));
237 
238 @safe pure nothrow @nogc unittest
239 {
240     alias E = string;
241     alias I = int;
242     alias R = typeof(["a", "b"]);
243     static assert(isIterableOf!(R, E));
244     static assert(isIterableOfUnqual!(R, const(E)));
245     static assert(isIterableOfSomeString!(R));
246     static assert(!isIterableOf!(R, I));
247 }
248 
249 /// Useful aliases for combinations of range predicates.
250 enum isRandomAccessRangeOf(R, E) = isRandomAccessRange!R && is(ElementType!R == E);
251 enum isForwardRangeOf(R, E) = isForwardRange!R && is(ElementType!R == E);
252 enum isInputRangeOf(R, E) = isInputRange!R && is(ElementType!R == E);
253 enum isBidirectionalRangeOf(R, E) = isBidirectionalRange!R && is(ElementType!R == E);
254 enum isOutputRangeOf(R, E) = isOutputRange!R && is(ElementType!R == E);
255 enum isArrayOf(R, E) = isArray!R && is(ElementType!R == E);
256 enum isArrayOfSomeString(R) = isArray!R && isSomeString!(ElementType!R);
257 
258 unittest
259 {
260     alias R = typeof(["a", "b"]);
261     static assert(isArrayOf!(R, string));
262     static assert(isArrayOfSomeString!(R));
263 }
264 
265 alias isSource = isInputRange;
266 alias isRange = isInputRange;
267 alias isSourceOf = isInputRangeOf;
268 alias isSink = isOutputRange;
269 alias isSinkOf = isOutputRangeOf;
270 
271 enum isSourceOfSomeChar(R) = (isSource!R && isSomeChar!(ElementType!R));
272 alias isSomeCharSource = isSourceOfSomeChar;
273 alias isSomeLazyString = isSourceOfSomeChar;
274 
275 unittest
276 {
277     import std.meta : AliasSeq;
278     foreach (Ch; AliasSeq!(char, wchar, dchar))
279     {
280         assert(isSourceOfSomeChar!(Ch[]));
281         assert(isSourceOfSomeChar!(const(Ch)[]));
282         assert(isSourceOfSomeChar!(immutable(Ch)[]));
283     }
284 }
285 
286 enum isSourceOfSomeString(R) = (isSource!R && isSomeString!(ElementType!R));
287 alias isSomeStringSource = isSourceOfSomeString;
288 
289 import std.functional: unaryFun, binaryFun;
290 
291 /* TODO Do we need use of unaryFun and binaryFun here? */
292 alias isEven = unaryFun!(a => (a & 1) == 0); // Limit to Integers?
293 alias isOdd = unaryFun!(a => (a & 1) == 1); // Limit to Integers?
294 alias lessThan = binaryFun!((a, b) => a < b);
295 alias greaterThan = binaryFun!((a, b) => a > b);
296 
297 /** Check if $(D T) has an even length. */
298 enum hasEvenLength(T...) = !(T.length & 1);
299 unittest
300 {
301     static assert(!hasEvenLength!(1));
302     static assert(hasEvenLength!(1, 2));
303     static assert(!hasEvenLength!(1, 2, 3));
304     static assert(hasEvenLength!(1, 2, 3, 4));
305 }
306 
307 enum isSignedIntegral(T) = isIntegral!T && isSigned!T;
308 enum isUnsignedIntegral(T) = isIntegral!T && isUnsigned!T;
309 
310 enum isString (T) = is(T == string);
311 enum isWString(T) = is(T == wstring);
312 enum isDString(T) = is(T == dstring);
313 
314 enum isEnum(T) = is(T == enum);
315 unittest
316 {
317     interface I {}
318     class A {}
319     class B( T ) {}
320     class C : B!int, I {}
321     struct S {}
322     enum E { X }
323     static assert(!isEnum!A );
324     static assert(!isEnum!( B!int ) );
325     static assert(!isEnum!C );
326     static assert(!isEnum!I );
327     static assert(isEnum!E );
328     static assert(!isEnum!int );
329     static assert(!isEnum!( int* ) );
330 }
331 
332 /* See also: http://d.puremagic.com/issues/show_bug.cgi?id=4427 */
333 enum isStruct(T) = is(T == struct);
334 unittest
335 {
336     interface I {}
337     class A {}
338     class B( T ) {}
339     class C : B!int, I {}
340     struct S {}
341     static assert(!isStruct!A );
342     static assert(!isStruct!( B!int ) );
343     static assert(!isStruct!C );
344     static assert(!isStruct!I );
345     static assert(isStruct!S );
346     static assert(!isStruct!int );
347     static assert(!isStruct!( int* ) );
348 }
349 
350 enum isClass(T) = is(T == class);
351 unittest
352 {
353     interface I {}
354     class A {}
355     class B( T ) {}
356     class C : B!int, I {}
357     struct S {}
358     static assert(isClass!A );
359     static assert(isClass!( B!int ) );
360     static assert(isClass!C );
361     static assert(!isClass!I );
362     static assert(!isClass!S );
363     static assert(!isClass!int );
364     static assert(!isClass!( int* ) );
365 }
366 
367 enum isInterface(T) = is(T == interface);
368 unittest
369 {
370     interface I {}
371     class A {}
372     class B( T ) {}
373     class C : B!int, I {}
374     struct S {}
375     static assert(!isInterface!A );
376     static assert(!isInterface!( B!int ) );
377     static assert(!isInterface!C );
378     static assert(isInterface!I );
379     static assert(!isInterface!S );
380     static assert(!isInterface!int );
381     static assert(!isInterface!( int* ) );
382 }
383 
384 template isType(T)       { enum isType = true; }
385 template isType(alias T) { enum isType = false; }
386 
387 unittest
388 {
389     struct S { alias int foo; }
390     static assert(isType!int );
391     static assert(isType!float );
392     static assert(isType!string );
393     //static assert(isType!S ); // Bugzilla 4431
394     static assert(isType!( S.foo ) );
395     static assert(!isType!4 );
396     static assert(!isType!"Hello world!" );
397 }
398 
399 /** Note that `NotNull!T` is not `isNullable` :) */
400 template isNullable(T)
401 {
402     import std.traits: isAssignable;
403     enum isNullable = isAssignable!(T, typeof(null));
404 }
405 ///
406 unittest
407 {
408     static assert(isNullable!(int*));
409 }
410 
411 enum nameOf(alias a) = a.stringof;
412 ///
413 unittest
414 {
415     int var;
416     static assert(nameOf!var == var.stringof);
417 }
418 
419 /** Is $(D ElementType) of type of $(D a). */
420 alias ElementTypeOf(alias a) = ElementType!(typeof(a));
421 alias elTypeOf = ElementTypeOf;
422 ///
423 unittest
424 {
425     int[] var;
426     static assert(is(ElementTypeOf!var == int));
427 }
428 
429 template Chainable()
430 {
431     import std.range: chain;
432     auto ref opCast(Range)(Range r)
433     {
434         return chain(this, r);
435     }
436 }
437 unittest { mixin Chainable; }
438 
439 /** Returns true if `T` is an instance of the template `S`.
440     See also: http://forum.dlang.org/thread/mailman.2901.1316118301.14074.digitalmars-d-learn@puremagic.com#post-zzdpfhsgfdgpszdbgbbt:40forum.dlang.org
441 */
442 template isA(alias S, T)
443 {
444     import std.traits : isInstanceOf;
445     enum isA = isInstanceOf!(S, T);
446 }
447 
448 unittest
449 {
450     import std.traits : isInstanceOf;
451     import std.range : SortedRange, assumeSorted;
452     const x = [1, 2, 3].assumeSorted;
453     static assert(isInstanceOf!(SortedRange, typeof(x)));
454     static assert(isA!(SortedRange, typeof(x)));
455 }
456 
457 /** See also: http://forum.dlang.org/thread/bug-6384-3@http.d.puremagic.com/issues/
458     See also: http://forum.dlang.org/thread/jrqiiicmtpenzokfxvlz@forum.dlang.org */
459 enum isOpBinary(T, string op, U) = is(typeof(mixin("T.init" ~ op ~ "U.init")));
460 
461 enum isComparable(T) = is(typeof({ return T.init <  T.init; }));
462 enum isEquable   (T) = is(typeof({ return T.init == T.init; }));
463 enum isNotEquable(T) = is(typeof({ return T.init != T.init; }));
464 
465 unittest
466 {
467     static assert(isComparable!int);
468     static assert(isComparable!string);
469     static assert(!isComparable!creal);
470     static struct Foo {}
471     static assert(!isComparable!Foo);
472     static struct Bar { bool opCmp(Bar) { return true; } }
473     static assert(isComparable!Bar);
474 }
475 
476 // TODO Make variadic
477 enum areComparable(T, U) = is(typeof({ return T.init <  U.init; }));
478 enum areEquable   (T, U) = is(typeof({ return T.init == U.init; }));
479 enum areNotEquable(T, U) = is(typeof({ return T.init != U.init; }));
480 
481 unittest
482 {
483     static assert(areComparable!(int, float));
484     static assert(areEquable!(int, float));
485     static assert(areNotEquable!(int, float));
486 
487     static assert(!areComparable!(int, string));
488     static assert(!areEquable!(int, string));
489     static assert(!areNotEquable!(int, string));
490 }
491 
492 enum isValueType(T) = !hasIndirections!T;
493 // enum isValueType(T) = isScalarType!T || isStaticArray!T || isStruct!T;
494 enum hasValueSemantics(T) = !hasIndirections!T; // TODO merge with isValueType
495 
496 /* See also: http://forum.dlang.org/thread/hsfkgcmkjgvrfuyjoujj@forum.dlang.org#post-hsfkgcmkjgvrfuyjoujj:40forum.dlang.org */
497 enum isReferenceType(T) = isDynamicArray!T || isSomeString!T || isClass!T;
498 
499 enum arityMin0(alias fun) = __traits(compiles, fun());
500 
501 /** TODO Unite into a variadic.
502     See also: http://forum.dlang.org/thread/bfjwbhkyehcloqcjzxck@forum.dlang.org#post-atjmewbffdzeixrviyoa:40forum.dlang.org
503 */
504 enum isCallableWith(alias fun, T) = (is(typeof(fun(T.init))) ||
505                                      is(typeof(T.init.fun))); // TODO Are both these needed?
506 unittest
507 {
508     auto sqr(T)(T x) { return x*x; }
509     assert(isCallableWith!(sqr, int));
510     assert(!isCallableWith!(sqr, string));
511 }
512 
513 /* TODO Unite into a variadic.
514    See also: http://forum.dlang.org/thread/bfjwbhkyehcloqcjzxck@forum.dlang.org#post-atjmewbffdzeixrviyoa:40forum.dlang.org
515  */
516 enum isCallableWith(alias fun, T, U) = (is(typeof(fun(T.init,
517                                                       U.init))) ||
518                                         is(typeof(T.init.fun(U)))); // TODO Are both these needed?
519 unittest
520 {
521     auto sqr2(T)(T x, T y) { return x*x + y*y; }
522     assert(isCallableWith!(sqr2, int, int));
523     assert(!isCallableWith!(sqr2, int, string));
524 }
525 
526 /** Check if $(D T) is a Sorted Range.
527     See also: http://forum.dlang.org/thread/lt1g3q$15fe$1@digitalmars.com
528 */
529 template isSortedRange(T)
530 {
531     import std.traits : isInstanceOf;
532     import std.range: SortedRange;
533     enum isSortedRange = isInstanceOf!(SortedRange, T); // TODO Or use: __traits(isSame, TemplateOf!R, SortedRange)
534 }
535 
536 /** Check if Function $(D expr) is callable at compile-time.
537     See also: http://forum.dlang.org/thread/owlwzvidwwpsrelpkbok@forum.dlang.org
538 */
539 template isCTFEable(alias fun)
540 {
541     template isCTFEable_aux(alias T)
542     {
543         enum isCTFEable_aux = T;
544     }
545     enum isCTFEable = __traits(compiles, isCTFEable_aux!(fun()));
546 }
547 
548 template isCTFEable2(fun...)
549 {
550     enum isCTFEable2 = true;
551 }
552 
553 unittest
554 {
555     int fun1() { return 1; }
556     auto fun1_N()
557     {
558         import std.array;
559 //would return Error: gc_malloc cannot be interpreted at compile time,
560         /* because it has no available source code due to a bug */
561             return [1].array;
562     }
563     int fun2(int x)
564     {
565         return 1;
566     }
567     auto fun2_N(int x){
568         import std.array;
569 //same as fun1_N
570         return [1].array;
571     }
572 
573     int a1;
574     enum a2=0;
575 
576     static assert(!isCTFEable!(()=>a1));
577     static assert(isCTFEable!(()=>a2));
578 
579     static assert(isCTFEable!fun1);
580     /* static assert(!isCTFEable!fun1_N); */
581 
582     static assert(isCTFEable!(()=>fun2(0)));
583     /* static assert(!isCTFEable!(()=>fun2_N(0))); */
584 //NOTE:an alternate syntax which could be implemented would be: static
585     /* assert(!isCTFEable!(fun2_N,0)); */
586 }
587 
588 /** Check if the value of $(D expr) is known at compile-time.
589     See also: http://forum.dlang.org/thread/owlwzvidwwpsrelpkbok@forum.dlang.org
590 */
591 enum isCTEable(alias expr) = __traits(compiles, { enum id = expr; });
592 
593 unittest
594 {
595     static assert(isCTEable!11);
596     enum x = 11;
597     static assert(isCTEable!x);
598     auto y = 11;
599     static assert(!isCTEable!y);
600 }
601 
602 import std.traits: functionAttributes, FunctionAttribute, isCallable, ParameterTypeTuple, Unqual;
603 
604 /** Returns $(D true) if $(D T) is not $(D const) or $(D immutable).
605     Note that isConst is true for string, or immutable(char)[], because the
606     'head' is mutable.
607 */
608 import std.traits : isMutable;
609 enum isConst(T) = !isMutable!T;
610 
611 unittest
612 {
613     static assert(isConst!(const(int)));
614     static assert(!isConst!int);
615 }
616 
617 import std.traits : CommonType;
618 
619 enum bool haveCommonType(Types...) = !is(CommonType!Types == void);
620 unittest
621 {
622     static assert(haveCommonType!(bool, int, long));
623     static assert(!haveCommonType!(bool, int, string));
624 }
625 
626 /** Check if $(D fun) is a pure function. */
627 enum bool isPure(alias fun) = (isCallable!fun &&
628                                (functionAttributes!fun &
629                                 FunctionAttribute.pure_));
630 
631 /** Check if $(D fun) is a function purely callable with arguments T. */
632 enum bool isPurelyCallableWith(alias fun, T...) = (isPure!fun &&
633                                                    is(T == ParameterTypeTuple!fun));
634 
635 unittest
636 {
637     int foo(int x) @safe pure nothrow { return x; }
638     static assert(isPure!foo);
639     static assert(isPurelyCallableWith!(foo, int));
640 }
641 
642 /** Persistently Call Function $(D fun) with arguments $(D args).
643 
644     Hash Id Build-Timestamp (Code-Id because we currently have stable way of hashing-algorithms) is Constructed from Data Structure:
645     - Hierarchically Mangled Unqual!typeof(instance)
646     - Use msgpack in combination with sha1Of or only sha1Of (with extended
647     overloads for sha1Of) if available.
648 
649     Extend std.functional : memoize to accept pure functions that takes an
650     immutable mmap as input. Create wrapper that converts file to immutable mmap
651     and performs memoization on the pure function.
652 
653 */
654 auto persistentlyMemoizedCall(alias fun, T...)(T args)
655     if (isPure!fun &&
656         isCallable!(fun, args))
657 {
658     import std.functional: memoize;
659     return fun(args);
660 }
661 
662 /** Move std.uni.newLine?
663     TODO What to do with Windows style endings?
664     See also: https://en.wikipedia.org/wiki/Newline
665 */
666 bool isNewline(C)(C c) @safe pure nothrow @nogc
667     if (isSomeChar!C)
668 {
669     import std.ascii: newline; // TODO Probably not useful.
670     static if (newline == "\n")
671     {
672         return (c == '\n' || c == '\r'); // optimized for systems with \n as default
673     }
674     else static if (newline == "\r")
675     {
676         return (c == '\r' || c == '\n'); // optimized for systems with \r as default
677     }
678     else
679     {
680         static assert(false, "Support Windows?");
681     }
682 }
683 
684 bool isNewline(S)(S s) @safe pure nothrow @nogc
685     if (isSomeString!S)
686 {
687     import std.ascii: newline; // TODO Probably not useful.
688     static if (newline == "\n")
689     {
690         return (s == '\n' || s == '\r'); // optimized for systems with \n as default
691     }
692     else static if (newline == "\r")
693     {
694         return (s == '\r' || s == '\n'); // optimized for systems with \r as default
695     }
696     else static if (newline == "\r\n")
697     {
698         return (s == "\r\n" || s == '\r' || s == '\n'); // optimized for systems with \r\n as default
699     }
700     else static if (newline == "\n\r")
701     {
702         return (s == "\n\r" || s == '\r' || s == '\n'); // optimized for systems with \n\r as default
703     }
704     else
705     {
706         static assert(false, "Support windows?");
707     }
708 }
709 
710 /** Dynamic Variant of $(D EnumMembers).
711     See also: http://forum.dlang.org/thread/bspwlfypfishykezzocx@forum.dlang.org#post-dguqnroxbfewerepomwq:40forum.dlang.org
712 */
713 auto enumMembers(T)()
714 {
715     import std.traits: EnumMembers;
716     return [EnumMembers!T];
717 }
718 
719 /** Dynamic Variant of $(D EnumMembers) without Enumerator Aliases.
720     See also: http://forum.dlang.org/thread/bspwlfypfishykezzocx@forum.dlang.org#post-dguqnroxbfewerepomwq:40forum.dlang.org
721 */
722 auto uniqueEnumMembers(T)()
723 {
724     import std.traits: EnumMembers;
725     import std.algorithm: sort, uniq;
726     return [EnumMembers!T].sort().uniq;
727 }
728 
729 enum sizeOf(T) = T.sizeof;      // TODO Add to Phobos
730 template sizesOf(T...)          // TODO Add to Phobos
731 {
732     import std.meta : staticMap;
733     enum sizesOf = staticMap!(sizeOf, T);
734 }
735 
736 enum stringOf(T) = T.stringof;      // TODO Add to Phobos
737 template stringsOf(T...)          // TODO Add to Phobos
738 {
739     import std.meta : staticMap;
740     enum stringsOf = staticMap!(stringOf, T);
741 }
742 
743 unittest
744 {
745     enum sizes = sizesOf!(bool, short, int, long);
746 
747     // static use
748     static assert(sizes[0] == 1);
749     static assert(sizes[1] == 2);
750     static assert(sizes[2] == 4);
751     static assert(sizes[3] == 8);
752 
753     // dynamic use
754     const i = 0;
755     assert([sizes][i] == 1);
756 }
757 
758 /** Get Number of Bits Required to store an instance of $(D T).
759     See also: http://forum.dlang.org/thread/okonqhnxzqlqtxijxsfg@forum.dlang.org
760    */
761 template packedBitSizeOf(T)
762 {
763     static if (is(T == enum))
764     {
765         static assert(T.min != T.max, "enum T must have at least two enumerators");
766         import core.bitop : bsr;
767         enum range = T.max - T.min;
768         enum packedBitSizeOf = range.bsr + 1;
769     }
770     // TODO
771     // else static if (isAggregate!T)
772     // {
773     //     foreach (E; T.tupleof)
774     //     {
775     //         ....;
776     //     }
777     // }
778     else
779     {
780         enum packedBitSizeOf = 8*T.sizeof;
781     }
782 }
783 
784 unittest
785 {
786     static assert(packedBitSizeOf!ubyte == 8);
787     static assert(!__traits(compiles, { enum E1 { x } static assert(packedBitSizeOf!E1 == 1);}));
788     enum E2 { x, y }
789     static assert(packedBitSizeOf!E2 == 1);
790     enum E3 { x, y, z }
791     static assert(packedBitSizeOf!E3 == 2);
792     enum E4 { x, y, z, w }
793     static assert(packedBitSizeOf!E4 == 2);
794     enum E5 { a, b, c, d, e }
795     static assert(packedBitSizeOf!E5 == 3);
796     enum E6 { a, b, c, d, e, f }
797     static assert(packedBitSizeOf!E6 == 3);
798     enum E7 { a, b, c, d, e, f, g }
799     static assert(packedBitSizeOf!E7 == 3);
800     enum E8 { a, b, c, d, e, f, g, h }
801     static assert(packedBitSizeOf!E8 == 3);
802     enum E9 { a, b, c, d, e, f, g, h, i }
803     static assert(packedBitSizeOf!E9 == 4);
804 }
805 
806 /** Get Dimensionality of Type $(D T).
807    See also: http://forum.dlang.org/thread/hiuhqdxtpifhzwebewjh@forum.dlang.org?page=2
808 */
809 
810 template dimensionality (T)
811 {
812     template count_dim (uint i = 0)
813     {
814         static if (is(typeof(T.init.opSlice!i(0, 0))))
815         {
816             enum count_dim = count_dim!(i+1);
817         }
818         else static if (i == 0 &&
819                         (isInputRange!T ||
820                          is(typeof(T.init[0]))))
821         {
822             enum count_dim = 1;
823         }
824         else
825         {
826             enum count_dim = i;
827         }
828     }
829     alias dimensionality = count_dim!();
830 }
831 
832 unittest
833 {
834     static assert(dimensionality!(int[]) == 1);
835 }