summaryrefslogtreecommitdiff
path: root/meta
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2019-03-02 08:01:58 +0100
committerEugen Wissner <belka@caraus.de>2019-03-02 08:08:10 +0100
commit5b850d532edebf83d8988ff413cbd2a988011630 (patch)
treeed9c996e52939fcf4ea88518cd7cdb000e0869d9 /meta
parentd7dfa3f6f14309327934fa846aed6a5c3b40dd6a (diff)
downloadtanya-5b850d532edebf83d8988ff413cbd2a988011630.tar.gz
Move meta into a separate subpackage
Diffstat (limited to 'meta')
-rw-r--r--meta/dub.json5
-rw-r--r--meta/source/tanya/meta/metafunction.d1862
-rw-r--r--meta/source/tanya/meta/package.d23
-rw-r--r--meta/source/tanya/meta/trait.d3106
-rw-r--r--meta/source/tanya/meta/transform.d981
5 files changed, 5977 insertions, 0 deletions
diff --git a/meta/dub.json b/meta/dub.json
new file mode 100644
index 0000000..c8d2ba7
--- /dev/null
+++ b/meta/dub.json
@@ -0,0 +1,5 @@
+{
+ "name": "meta",
+ "description": "Template metaprogramming",
+ "targetType": "library"
+}
diff --git a/meta/source/tanya/meta/metafunction.d b/meta/source/tanya/meta/metafunction.d
new file mode 100644
index 0000000..6da6b47
--- /dev/null
+++ b/meta/source/tanya/meta/metafunction.d
@@ -0,0 +1,1862 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This module is suited for computations on template arguments, both types and
+ * values at compile time.
+ *
+ * It contains different algorithms for iterating, searching and modifying
+ * template arguments.
+ *
+ * Copyright: Eugene Wissner 2017-2018.
+ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
+ * Mozilla Public License, v. 2.0).
+ * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
+ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/meta/metafunction.d,
+ * tanya/meta/metafunction.d)
+ */
+module tanya.meta.metafunction;
+
+import tanya.meta.trait;
+import tanya.meta.transform;
+
+/**
+ * Finds the minimum value in $(D_PARAM Args) according to $(D_PARAM pred).
+ *
+ * $(D_PARAM Args) should contain at least one element.
+ *
+ * $(D_PARAM pred) can evaluate to:
+ * $(UL
+ * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
+ * $(D_INLINECODE Args[0] < Args[1]).)
+ * $(LI $(D_KEYWORD int): a negative number means that
+ * $(D_INLINECODE Args[0] < Args[1]), a positive number that
+ * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.)
+ * )
+ *
+ * Params:
+ * pred = Template predicate.
+ * Args = Elements for which you want to find the minimum value.
+ *
+ * Returns: The minimum.
+ *
+ * See_Also: $(D_PSYMBOL isLess).
+ */
+template Min(alias pred, Args...)
+if (Args.length > 0 && __traits(isTemplate, pred))
+{
+ static if (Args.length == 1)
+ {
+ alias Min = Alias!(Args[0]);
+ }
+ else static if (isLess!(pred, Args[1], Args[0]))
+ {
+ alias Min = Min!(pred, Args[1], Args[2 .. $]);
+ }
+ else
+ {
+ alias Min = Min!(pred, Args[0], Args[2 .. $]);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ enum bool cmp(alias T, alias U) = T < U;
+ static assert(Min!(cmp, 8, 4, 5, 3, 13) == 3);
+ static assert(Min!(cmp, 8) == 8);
+}
+
+/**
+ * Finds the maximum value in $(D_PARAM Args) according to $(D_PARAM pred).
+ *
+ * $(D_PARAM Args) should contain at least one element.
+ *
+ * $(D_PARAM pred) can evaluate to:
+ * $(UL
+ * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
+ * $(D_INLINECODE Args[0] < Args[1]).)
+ * $(LI $(D_KEYWORD int): a negative number means that
+ * $(D_INLINECODE Args[0] < Args[1]), a positive number that
+ * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.)
+ * )
+ *
+ * Params:
+ * pred = Template predicate.
+ * Args = Elements for which you want to find the maximum value.
+ *
+ * Returns: The maximum.
+ *
+ * See_Also: $(D_PSYMBOL isLess).
+ */
+template Max(alias pred, Args...)
+if (Args.length > 0 && __traits(isTemplate, pred))
+{
+ static if (Args.length == 1)
+ {
+ alias Max = Alias!(Args[0]);
+ }
+ else static if (isGreater!(pred, Args[1], Args[0]))
+ {
+ alias Max = Max!(pred, Args[1], Args[2 .. $]);
+ }
+ else
+ {
+ alias Max = Max!(pred, Args[0], Args[2 .. $]);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ enum bool cmp(alias T, alias U) = T < U;
+ static assert(Max!(cmp, 8, 4, 5, 3, 13) == 13);
+ static assert(Max!(cmp, 8) == 8);
+}
+
+/**
+ * Zips one or more $(D_PSYMBOL Pack)s with $(D_PARAM f).
+ *
+ * Given $(D_PARAM f) and tuples t1, t2, ..., tk, where tk[i] denotes the
+ * $(I i)-th element of the tuple $(I k)-th tuple, $(D_PSYMBOL ZipWith)
+ * produces a sequence:
+ *
+ * ---
+ * f(t1[0], t2[0], ... tk[0]),
+ * f(t1[1], t2[1], ... tk[1]),
+ * ...
+ * f(tk[0], tk[1], ... tk[i]),
+ * ---
+ *
+ * $(D_PSYMBOL ZipWith) begins with the first elements from $(D_PARAM Packs)
+ * and applies $(D_PARAM f) to them, then it takes the second
+ * ones and does the same, and so on.
+ *
+ * If not all argument tuples have the same length, $(D_PSYMBOL ZipWith) will
+ * zip only `n` elements from each tuple, where `n` is the length of the
+ * shortest tuple in the argument list. Remaining elements in the longer tuples
+ * are just ignored.
+ *
+ * Params:
+ * f = Some template that can be applied to the elements of
+ * $(D_PARAM Packs).
+ * Packs = $(D_PSYMBOL Pack) instances.
+ *
+ * Returns: A sequence, whose $(I i)-th element contains the $(I i)-th element
+ * from each of the $(D_PARAM Packs).
+ */
+template ZipWith(alias f, Packs...)
+if (Packs.length > 0
+ && __traits(isTemplate, f)
+ && (allSatisfy!(ApplyLeft!(isInstanceOf, Pack), Packs)
+ || allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Packs)))
+{
+ private template GetIth(size_t i, Args...)
+ {
+ static if ((Args.length == 0) || (Args[0].Seq.length <= i))
+ {
+ alias GetIth = AliasSeq!();
+ }
+ else
+ {
+ alias GetIth = AliasSeq!(Args[0].Seq[i], GetIth!(i, Args[1 .. $]));
+ }
+ }
+ private template Iterate(size_t i, Args...)
+ {
+ alias Pack = GetIth!(i, Args);
+
+ static if (Pack.length < Packs.length)
+ {
+ alias Iterate = AliasSeq!();
+ }
+ else
+ {
+ alias Iterate = AliasSeq!(f!Pack, Iterate!(i + 1, Args));
+ }
+ }
+ alias ZipWith = Iterate!(0, Packs);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias Result1 = ZipWith!(AliasSeq, Pack!(1, 2), Pack!(5, 6), Pack!(9, 10));
+ static assert(Result1 == AliasSeq!(1, 5, 9, 2, 6, 10));
+
+ alias Result2 = ZipWith!(AliasSeq, Pack!(1, 2, 3), Pack!(4, 5));
+ static assert(Result2 == AliasSeq!(1, 4, 2, 5));
+
+ alias Result3 = ZipWith!(AliasSeq, Pack!(), Pack!(4, 5));
+ static assert(Result3.length == 0);
+}
+
+/**
+ * Holds a typed sequence of template parameters.
+ *
+ * Different than $(D_PSYMBOL AliasSeq), $(D_PSYMBOL Pack) doesn't unpack
+ * its template parameters automatically. Consider:
+ *
+ * ---
+ * template A(Args...)
+ * {
+ * static assert(Args.length == 4);
+ * }
+ *
+ * alias AInstance = A!(AliasSeq!(int, uint), AliasSeq!(float, double));
+ * ---
+ *
+ * Using $(D_PSYMBOL AliasSeq) template `A` gets 4 parameters instead of 2,
+ * because $(D_PSYMBOL AliasSeq) is just an alias for its template parameters.
+ *
+ * With $(D_PSYMBOL Pack) it is possible to pass distinguishable
+ * sequences of parameters to a template. So:
+ *
+ * ---
+ * template B(Args...)
+ * {
+ * static assert(Args.length == 2);
+ * }
+ *
+ * alias BInstance = B!(Pack!(int, uint), Pack!(float, double));
+ * ---
+ *
+ * Params:
+ * Args = Elements of this $(D_PSYMBOL Pack).
+ *
+ * See_Also: $(D_PSYMBOL AliasSeq).
+ */
+struct Pack(Args...)
+{
+ /// Elements in this tuple as $(D_PSYMBOL AliasSeq).
+ alias Seq = Args;
+
+ /// The length of the tuple.
+ enum size_t length = Args.length;
+
+ alias Seq this;
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias A = Pack!short;
+ alias B = Pack!(3, 8, 9);
+ alias C = Pack!(A, B);
+
+ static assert(C.length == 2);
+
+ static assert(A.length == 1);
+ static assert(is(A.Seq == AliasSeq!short));
+ static assert(B.length == 3);
+ static assert(B.Seq == AliasSeq!(3, 8, 9));
+
+ alias D = Pack!();
+ static assert(D.length == 0);
+ static assert(is(D.Seq == AliasSeq!()));
+}
+
+/**
+ * Unordered sequence of unique aliases.
+ *
+ * $(D_PARAM Args) can contain duplicates, but they will be filtered out, so
+ * $(D_PSYMBOL Set) contains only unique items. $(D_PSYMBOL isEqual) is used
+ * for determining if two items are equal.
+ *
+ * Params:
+ * Args = Elements of this $(D_PSYMBOL Set).
+ */
+struct Set(Args...)
+{
+ /// Elements in this set as $(D_PSYMBOL AliasSeq).
+ alias Seq = NoDuplicates!Args;
+
+ /// The length of the set.
+ enum size_t length = Seq.length;
+
+ alias Seq this;
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias S1 = Set!(int, 5, 5, int, 4);
+ static assert(S1.length == 3);
+}
+
+/**
+ * Produces a $(D_PSYMBOL Set) containing all elements of the given
+ * $(D_PARAM Sets).
+ *
+ * Params:
+ * Sets = List of $(D_PSYMBOL Set) instances.
+ *
+ * Returns: Set-theoretic union of all $(D_PARAM Sets).
+ *
+ * See_Also: $(D_PSYMBOL Set).
+ */
+template Union(Sets...)
+if (allSatisfy!(ApplyLeft!(isInstanceOf, Set), Sets))
+{
+ private template Impl(Sets...)
+ {
+ static if (Sets.length == 0)
+ {
+ alias Impl = AliasSeq!();
+ }
+ else
+ {
+ alias Impl = AliasSeq!(Sets[0].Seq, Impl!(Sets[1 .. $]));
+ }
+ }
+ alias Union = Set!(Impl!Sets);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias S1 = Set!(2, 5, 8, 4);
+ alias S2 = Set!(3, 8, 4, 1);
+ static assert(Union!(S1, S2).Seq == AliasSeq!(2, 5, 8, 4, 3, 1));
+}
+
+/**
+ * Produces a $(D_PSYMBOL Set) that containing elements of
+ * $(D_INLINECODE Sets[0]) that are also elements of all other sets in
+ * $(D_PARAM Sets).
+ *
+ * Params:
+ * Sets = List of $(D_PSYMBOL Set) instances.
+ *
+ * Returns: Set-theoretic intersection of all $(D_PARAM Sets).
+ *
+ * See_Also: $(D_PSYMBOL Set).
+ */
+template Intersection(Sets...)
+if (allSatisfy!(ApplyLeft!(isInstanceOf, Set), Sets))
+{
+ private template Impl(Args...)
+ if (Args.length > 0)
+ {
+ alias Equal = ApplyLeft!(isEqual, Args[0]);
+ static if (Args.length == 1)
+ {
+ enum bool Impl = true;
+ }
+ else static if (!anySatisfy!(Equal, Args[1].Seq))
+ {
+ enum bool Impl = false;
+ }
+ else
+ {
+ enum bool Impl = Impl!(Args[0], Args[2 .. $]);
+ }
+ }
+
+ private enum bool FilterImpl(Args...) = Impl!(Args[0], Sets[1 .. $]);
+
+ static if (Sets.length == 0)
+ {
+ alias Intersection = Set!();
+ }
+ else
+ {
+ alias Intersection = Set!(Filter!(FilterImpl, Sets[0].Seq));
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias S1 = Set!(2, 5, 8, 4);
+ alias S2 = Set!(3, 8, 4, 1);
+ static assert(Intersection!(S1, S2).Seq == AliasSeq!(8, 4));
+
+ static assert(Intersection!(S1).Seq == AliasSeq!(2, 5, 8, 4));
+ static assert(Intersection!().length == 0);
+}
+
+/**
+ * Produces a $(D_PSYMBOL Set) that contains all elements of
+ * $(D_PARAM S1) that are not members of $(D_PARAM S2).
+ *
+ * Params:
+ * S1 = A $(D_PSYMBOL Set).
+ * S2 = A $(D_PSYMBOL Set).
+ *
+ * Returns: Set-theoretic difference of two sets $(D_PARAM S1) and
+ * $(D_PARAM S2).
+ *
+ * See_Also: $(D_PSYMBOL Set).
+ */
+template Difference(alias S1, alias S2)
+if (isInstanceOf!(Set, S1) && isInstanceOf!(Set, S2))
+{
+ private template Impl(Args...)
+ {
+ alias Equal = ApplyLeft!(isEqual, Args[0]);
+ enum bool Impl = !anySatisfy!(Equal, S2.Seq);
+ }
+
+ static if (S1.length == 0)
+ {
+ alias Difference = Set!();
+ }
+ else static if (S2.length == 1)
+ {
+ alias Difference = S1;
+ }
+ else
+ {
+ alias Difference = Set!(Filter!(Impl, S1.Seq));
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias S1 = Set!(2, 5, 8, 4);
+ alias S2 = Set!(3, 8, 4, 1);
+ static assert(Difference!(S1, S2).Seq == AliasSeq!(2, 5));
+ static assert(Difference!(S2, S1).Seq == AliasSeq!(3, 1));
+ static assert(Difference!(S1, Set!()).Seq == AliasSeq!(2, 5, 8, 4));
+}
+
+/**
+ * Tests whether $(D_INLINECODE Args[0]) is less than or equal to
+ * $(D_INLINECODE Args[1]) according to $(D_PARAM cmp).
+ *
+ * $(D_PARAM cmp) can evaluate to:
+ * $(UL
+ * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
+ * $(D_INLINECODE Args[0] < Args[1]).)
+ * $(LI $(D_KEYWORD int): a negative number means that
+ * $(D_INLINECODE Args[0] < Args[1]), a positive number that
+ * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.)
+ * )
+ *
+ * Params:
+ * Args = Two aliases to compare for equality.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is less than or equal
+ * to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
+ */
+template isLessEqual(alias cmp, Args...)
+if (Args.length == 2 && __traits(isTemplate, cmp))
+{
+ private enum result = cmp!(Args[1], Args[0]);
+ static if (is(typeof(result) == bool))
+ {
+ enum bool isLessEqual = !result;
+ }
+ else
+ {
+ enum bool isLessEqual = result >= 0;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ enum bool boolCmp(T, U) = T.sizeof < U.sizeof;
+ static assert(isLessEqual!(boolCmp, byte, int));
+ static assert(isLessEqual!(boolCmp, uint, int));
+ static assert(!isLessEqual!(boolCmp, long, int));
+
+ enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof;
+ static assert(isLessEqual!(intCmp, byte, int));
+ static assert(isLessEqual!(intCmp, uint, int));
+ static assert(!isLessEqual!(intCmp, long, int));
+}
+
+/**
+ * Tests whether $(D_INLINECODE Args[0]) is greater than or equal to
+ * $(D_INLINECODE Args[1]) according to $(D_PARAM cmp).
+ *
+ * $(D_PARAM cmp) can evaluate to:
+ * $(UL
+ * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
+ * $(D_INLINECODE Args[0] < Args[1]).)
+ * $(LI $(D_KEYWORD int): a negative number means that
+ * $(D_INLINECODE Args[0] < Args[1]), a positive number that
+ * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.)
+ * )
+ *
+ * Params:
+ * Args = Two aliases to compare for equality.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is greater than or
+ * equal to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
+ */
+template isGreaterEqual(alias cmp, Args...)
+if (Args.length == 2 && __traits(isTemplate, cmp))
+{
+ private enum result = cmp!Args;
+ static if (is(typeof(result) == bool))
+ {
+ enum bool isGreaterEqual = !result;
+ }
+ else
+ {
+ enum bool isGreaterEqual = result >= 0;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ enum bool boolCmp(T, U) = T.sizeof < U.sizeof;
+ static assert(!isGreaterEqual!(boolCmp, byte, int));
+ static assert(isGreaterEqual!(boolCmp, uint, int));
+ static assert(isGreaterEqual!(boolCmp, long, int));
+
+ enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof;
+ static assert(!isGreaterEqual!(intCmp, byte, int));
+ static assert(isGreaterEqual!(intCmp, uint, int));
+ static assert(isGreaterEqual!(intCmp, long, int));
+}
+
+/**
+ * Tests whether $(D_INLINECODE Args[0]) is less than
+ * $(D_INLINECODE Args[1]) according to $(D_PARAM cmp).
+ *
+ * $(D_PARAM cmp) can evaluate to:
+ * $(UL
+ * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
+ * $(D_INLINECODE Args[0] < Args[1]).)
+ * $(LI $(D_KEYWORD int): a negative number means that
+ * $(D_INLINECODE Args[0] < Args[1]), a positive number that
+ * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.)
+ * )
+ *
+ * Params:
+ * Args = Two aliases to compare for equality.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is less than
+ * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
+ */
+template isLess(alias cmp, Args...)
+if (Args.length == 2 && __traits(isTemplate, cmp))
+{
+ private enum result = cmp!Args;
+ static if (is(typeof(result) == bool))
+ {
+ enum bool isLess = result;
+ }
+ else
+ {
+ enum bool isLess = result < 0;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ enum bool boolCmp(T, U) = T.sizeof < U.sizeof;
+ static assert(isLess!(boolCmp, byte, int));
+ static assert(!isLess!(boolCmp, uint, int));
+ static assert(!isLess!(boolCmp, long, int));
+
+ enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof;
+ static assert(isLess!(intCmp, byte, int));
+ static assert(!isLess!(intCmp, uint, int));
+ static assert(!isLess!(intCmp, long, int));
+}
+
+/**
+ * Tests whether $(D_INLINECODE Args[0]) is greater than
+ * $(D_INLINECODE Args[1]) according to $(D_PARAM cmp).
+ *
+ * $(D_PARAM cmp) can evaluate to:
+ * $(UL
+ * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
+ * $(D_INLINECODE Args[0] < Args[1]).)
+ * $(LI $(D_KEYWORD int): a negative number means that
+ * $(D_INLINECODE Args[0] < Args[1]), a positive number that
+ * $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.)
+ * )
+ *
+ * Params:
+ * Args = Two aliases to compare for equality.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is greater than
+ * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
+ */
+template isGreater(alias cmp, Args...)
+if (Args.length == 2 && __traits(isTemplate, cmp))
+{
+ private enum result = cmp!Args;
+ static if (is(typeof(result) == bool))
+ {
+ enum bool isGreater = !result && cmp!(Args[1], Args[0]);
+ }
+ else
+ {
+ enum bool isGreater = result > 0;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ enum bool boolCmp(T, U) = T.sizeof < U.sizeof;
+ static assert(!isGreater!(boolCmp, byte, int));
+ static assert(!isGreater!(boolCmp, uint, int));
+ static assert(isGreater!(boolCmp, long, int));
+
+ enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof;
+ static assert(!isGreater!(intCmp, byte, int));
+ static assert(!isGreater!(intCmp, uint, int));
+ static assert(isGreater!(intCmp, long, int));
+}
+
+/**
+ * Tests whether $(D_INLINECODE Args[0]) is equal to $(D_INLINECODE Args[1]).
+ *
+ * $(D_PSYMBOL isEqual) checks first if $(D_PARAM Args) can be compared directly. If not, they are compared as types:
+ * $(D_INLINECODE is(Args[0] == Args[1])). It it fails, the arguments are
+ * considered to be not equal.
+ *
+ * If two items cannot be compared (for example comparing a type with a
+ * number), they are considered not equal.
+ *
+ * Params:
+ * Args = Two aliases to compare for equality.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is equal to
+ * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
+ */
+template isEqual(Args...)
+if (Args.length == 2)
+{
+ static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1]))
+ || (isTypeTuple!Args && is(Args[0] == Args[1]))
+ || __traits(isSame, Args[0], Args[1]))
+ {
+ enum bool isEqual = true;
+ }
+ else
+ {
+ enum bool isEqual = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isEqual!(int, int));
+ static assert(isEqual!(8, 8));
+ static assert(!isEqual!(int, const(int)));
+ static assert(!isEqual!(5, int));
+ static assert(!isEqual!(5, 8));
+}
+
+/**
+ * Tests whether $(D_INLINECODE Args[0]) isn't equal to
+ * $(D_INLINECODE Args[1]).
+ *
+ * $(D_PSYMBOL isNotEqual) checks first if $(D_PARAM Args) can be compared directly. If not, they are compared as types:
+ * $(D_INLINECODE is(Args[0] == Args[1])). It it fails, the arguments are
+ * considered to be not equal.
+ *
+ * Params:
+ * Args = Two aliases to compare for equality.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) isn't equal to
+ * $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
+ */
+template isNotEqual(Args...)
+if (Args.length == 2)
+{
+ enum bool isNotEqual = !isEqual!Args;
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(!isNotEqual!(int, int));
+ static assert(isNotEqual!(5, int));
+ static assert(isNotEqual!(5, 8));
+}
+
+/**
+ * Instantiates the template $(D_PARAM T) with $(D_PARAM Args).
+ *
+ * Params:
+ * T = Template.
+ * Args = Template parameters.
+ *
+ * Returns: Instantiated template.
+ */
+alias Instantiate(alias T, Args...) = T!Args;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ template Template(T)
+ {
+ alias Template = T;
+ }
+ alias Seq = AliasSeq!(Template, Template);
+
+ alias Instance1 = Instantiate!(Seq[0], int);
+ static assert(is(Instance1 == int));
+
+ alias Instance2 = Instantiate!(Seq[1], float);
+ static assert(is(Instance2 == float));
+}
+
+/**
+ * Creates an alias for $(D_PARAM T).
+ *
+ * In contrast to the $(D_KEYWORD alias)-keyword $(D_PSYMBOL Alias) can alias
+ * any kind of D symbol that can be used as argument to template alias
+ * parameters.
+ *
+ * $(UL
+ * $(LI Types)
+ * $(LI Local and global names)
+ * $(LI Module names)
+ * $(LI Template names)
+ * $(LI Template instance names)
+ * $(LI Literals)
+ * )
+ *
+ * Params:
+ * T = A symbol.
+ *
+ * Returns: An alias for $(D_PARAM T).
+ *
+ * See_Also: $(LINK2 https://dlang.org/spec/template.html#aliasparameters,
+ * Template Alias Parameters).
+ */
+alias Alias(alias T) = T;
+
+/// ditto
+alias Alias(T) = T;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Alias!int));
+
+ static assert(is(typeof(Alias!5)));
+ static assert(is(typeof(Alias!(() {}))));
+
+ int i;
+ static assert(is(typeof(Alias!i)));
+}
+
+/**
+ * Holds a sequence of aliases.
+ *
+ * $(D_PSYMBOL AliasSeq) can be used to pass multiple parameters to a template
+ * at once. $(D_PSYMBOL AliasSeq) behaves as it were just $(D_PARAM Args). Note
+ * that because of this property, if multiple instances of
+ * $(D_PSYMBOL AliasSeq) are passed to a template, they are not distinguishable
+ * from each other and act as a single sequence. There is also no way to make
+ * $(D_PSYMBOL AliasSeq) nested, it always unpacks its elements.
+ *
+ * Params:
+ * Args = Symbol sequence.
+ *
+ * Returns: An alias for sequence $(D_PARAM Args).
+ *
+ * See_Also: $(D_PSYMBOL Alias).
+ */
+alias AliasSeq(Args...) = Args;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(typeof({ alias T = AliasSeq!(short, 5); })));
+ static assert(is(typeof({ alias T = AliasSeq!(int, short, 5); })));
+ static assert(is(typeof({ alias T = AliasSeq!(() {}, short, 5); })));
+ static assert(is(typeof({ alias T = AliasSeq!(); })));
+
+ static assert(AliasSeq!().length == 0);
+ static assert(AliasSeq!(int, short, 5).length == 3);
+
+ alias A = AliasSeq!(short, float);
+ alias B = AliasSeq!(ushort, double);
+ alias C = AliasSeq!(A, B);
+ static assert(C.length == 4);
+}
+
+/**
+ * Tests whether all the items of $(D_PARAM L) satisfy the condition
+ * $(D_PARAM F).
+ *
+ * $(D_PARAM F) is a template that accepts one parameter and returns a boolean,
+ * so $(D_INLINECODE F!([0]) && F!([1])) and so on, can be called.
+ *
+ * Params:
+ * F = Template predicate.
+ * L = List of items to test.
+ *
+ * Returns: $(D_KEYWORD true) if all the items of $(D_PARAM L) satisfy
+ * $(D_PARAM F), $(D_KEYWORD false) otherwise.
+ */
+template allSatisfy(alias F, L...)
+if (__traits(isTemplate, F))
+{
+ static if (L.length == 0)
+ {
+ enum bool allSatisfy = true;
+ }
+ else static if (F!(L[0]))
+ {
+ enum bool allSatisfy = allSatisfy!(F, L[1 .. $]);
+ }
+ else
+ {
+ enum bool allSatisfy = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(allSatisfy!(isSigned, int, short, byte, long));
+ static assert(!allSatisfy!(isUnsigned, uint, ushort, float, ulong));
+}
+
+/**
+ * Tests whether any of the items of $(D_PARAM L) satisfy the condition
+ * $(D_PARAM F).
+ *
+ * $(D_PARAM F) is a template that accepts one parameter and returns a boolean,
+ * so $(D_INLINECODE F!([0]) && F!([1])) and so on, can be called.
+ *
+ * Params:
+ * F = Template predicate.
+ * L = List of items to test.
+ *
+ * Returns: $(D_KEYWORD true) if any of the items of $(D_PARAM L) satisfy
+ * $(D_PARAM F), $(D_KEYWORD false) otherwise.
+ */
+template anySatisfy(alias F, L...)
+if (__traits(isTemplate, F))
+{
+ static if (L.length == 0)
+ {
+ enum bool anySatisfy = false;
+ }
+ else static if (F!(L[0]))
+ {
+ enum bool anySatisfy = true;
+ }
+ else
+ {
+ enum bool anySatisfy = anySatisfy!(F, L[1 .. $]);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(anySatisfy!(isSigned, int, short, byte, long));
+ static assert(anySatisfy!(isUnsigned, uint, ushort, float, ulong));
+ static assert(!anySatisfy!(isSigned, uint, ushort, ulong));
+}
+
+private template indexOf(ptrdiff_t i, Args...)
+if (Args.length > 0)
+{
+ static if (Args.length == 1)
+ {
+ enum ptrdiff_t indexOf = -1;
+ }
+ else static if (isEqual!(Args[0 .. 2]))
+ {
+ enum ptrdiff_t indexOf = i;
+ }
+ else
+ {
+ enum ptrdiff_t indexOf = indexOf!(i + 1,
+ AliasSeq!(Args[0], Args[2 .. $]));
+ }
+}
+
+/**
+ * Returns the index of the first occurrence of $(D_PARAM T) in $(D_PARAM L).
+ * `-1` is returned if $(D_PARAM T) is not found.
+ *
+ * Params:
+ * T = The item to search for.
+ * L = Symbol sequence.
+ *
+ * Returns: The index of the first occurrence of $(D_PARAM T) in $(D_PARAM L).
+ */
+template staticIndexOf(T, L...)
+{
+ enum ptrdiff_t staticIndexOf = indexOf!(0, AliasSeq!(T, L));
+}
+
+/// ditto
+template staticIndexOf(alias T, L...)
+{
+ enum ptrdiff_t staticIndexOf = indexOf!(0, AliasSeq!(T, L));
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(staticIndexOf!(int) == -1);
+ static assert(staticIndexOf!(int, int) == 0);
+ static assert(staticIndexOf!(int, float, double, int, real) == 2);
+ static assert(staticIndexOf!(3, () {}, uint, 5, 3) == 3);
+}
+
+/**
+ * Looks for $(D_PARAM T) in $(D_PARAM L) and returns $(D_KEYWORD true) if it
+ * could be found and $(D_KEYWORD false) otherwise.
+ *
+ * Params:
+ * T = The item to search for.
+ * L = Symbol sequence.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) can be found in $(D_PARAM L),
+ * $(D_KEYWORD false) otherwise.
+ */
+template canFind(T, L...)
+{
+ enum bool canFind = indexOf!(0, AliasSeq!(T, L)) != -1;
+}
+
+/// ditto
+template canFind(alias T, L...)
+{
+ enum bool canFind = indexOf!(0, AliasSeq!(T, L)) != -1;
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(!canFind!(int));
+ static assert(canFind!(int, int));
+ static assert(canFind!(int, float, double, int, real));
+ static assert(canFind!(3, () {}, uint, 5, 3));
+}
+
+/*
+ * Tests whether $(D_PARAM T) is a template.
+ *
+ * $(D_PSYMBOL isTemplate) isn't $(D_KEYWORD true) for template instances,
+ * since the latter already represent some type. Only not instantiated
+ * templates, i.e. that accept some template parameters, are considered
+ * templates.
+ *
+ * Params:
+ * T = A symbol.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a template,
+ * $(D_KEYWORD false) otherwise.
+ */
+private enum bool isTemplate(alias T) = __traits(isTemplate, T);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct S(T)
+ {
+ }
+ static assert(isTemplate!S);
+ static assert(!isTemplate!(S!int));
+}
+
+/**
+ * Combines multiple templates with logical AND. So $(D_PSYMBOL templateAnd)
+ * evaluates to $(D_INLINECODE Preds[0] && Preds[1] && Preds[2]) and so on.
+ *
+ * Empty $(D_PARAM Preds) evaluates to $(D_KEYWORD true).
+ *
+ * Params:
+ * Preds = Template predicates.
+ *
+ * Returns: The constructed template.
+ */
+template templateAnd(Preds...)
+if (allSatisfy!(isTemplate, Preds))
+{
+ template templateAnd(T...)
+ {
+ static if (Preds.length == 0)
+ {
+ enum bool templateAnd = true;
+ }
+ else static if (Instantiate!(Preds[0], T))
+ {
+ alias templateAnd = Instantiate!(.templateAnd!(Preds[1 .. $]), T);
+ }
+ else
+ {
+ enum bool templateAnd = false;
+ }
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias isMutableInt = templateAnd!(isIntegral, isMutable);
+ static assert(isMutableInt!int);
+ static assert(!isMutableInt!(const int));
+ static assert(!isMutableInt!float);
+
+ alias alwaysTrue = templateAnd!();
+ static assert(alwaysTrue!int);
+
+ alias isIntegral = templateAnd!(.isIntegral);
+ static assert(isIntegral!int);
+ static assert(isIntegral!(const int));
+ static assert(!isIntegral!float);
+}
+
+/**
+ * Combines multiple templates with logical OR. So $(D_PSYMBOL templateOr)
+ * evaluates to $(D_INLINECODE Preds[0] || Preds[1] || Preds[2]) and so on.
+ *
+ * Empty $(D_PARAM Preds) evaluates to $(D_KEYWORD false).
+ *
+ * Params:
+ * Preds = Template predicates.
+ *
+ * Returns: The constructed template.
+ */
+template templateOr(Preds...)
+if (allSatisfy!(isTemplate, Preds))
+{
+ template templateOr(T...)
+ {
+ static if (Preds.length == 0)
+ {
+ enum bool templateOr = false;
+ }
+ else static if (Instantiate!(Preds[0], T))
+ {
+ enum bool templateOr = true;
+ }
+ else
+ {
+ alias templateOr = Instantiate!(.templateOr!(Preds[1 .. $]), T);
+ }
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias isMutableOrInt = templateOr!(isIntegral, isMutable);
+ static assert(isMutableOrInt!int);
+ static assert(isMutableOrInt!(const int));
+ static assert(isMutableOrInt!float);
+ static assert(!isMutableOrInt!(const float));
+
+ alias alwaysFalse = templateOr!();
+ static assert(!alwaysFalse!int);
+
+ alias isIntegral = templateOr!(.isIntegral);
+ static assert(isIntegral!int);
+ static assert(isIntegral!(const int));
+ static assert(!isIntegral!float);
+}
+
+/**
+ * Params:
+ * pred = Template predicate.
+ *
+ * Returns: Negated $(D_PARAM pred).
+ */
+template templateNot(alias pred)
+if (__traits(isTemplate, pred))
+{
+ enum bool templateNot(T...) = !pred!T;
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias isNotIntegral = templateNot!isIntegral;
+ static assert(!isNotIntegral!int);
+ static assert(isNotIntegral!(char[]));
+}
+
+/**
+ * Tests whether $(D_PARAM L) is sorted in ascending order according to
+ * $(D_PARAM cmp).
+ *
+ * $(D_PARAM cmp) can evaluate to:
+ * $(UL
+ * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
+ * $(D_INLINECODE a[i] < a[i + 1]).)
+ * $(LI $(D_KEYWORD int): a negative number means that
+ * $(D_INLINECODE a[i] < a[i + 1]), a positive number that
+ * $(D_INLINECODE a[i] > a[i + 1]), `0` if they equal.)
+ * )
+ *
+ * Params:
+ * cmp = Sorting template predicate.
+ * L = Elements to be tested.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM L) is sorted, $(D_KEYWORD false)
+ * if not.
+ */
+template isSorted(alias cmp, L...)
+if (__traits(isTemplate, cmp))
+{
+ static if (L.length <= 1)
+ {
+ enum bool isSorted = true;
+ }
+ else
+ {
+ // `L` is sorted if the both halves and the boundary values are sorted.
+ enum bool isSorted = isLessEqual!(cmp, L[$ / 2 - 1], L[$ / 2])
+ && isSorted!(cmp, L[0 .. $ / 2])
+ && isSorted!(cmp, L[$ / 2 .. $]);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ enum cmp(T, U) = T.sizeof < U.sizeof;
+ static assert(isSorted!(cmp));
+ static assert(isSorted!(cmp, byte));
+ static assert(isSorted!(cmp, byte, ubyte, short, uint));
+ static assert(!isSorted!(cmp, long, byte, ubyte, short, uint));
+}
+
+@nogc nothrow pure @safe unittest
+{
+ enum cmp(int x, int y) = x - y;
+ static assert(isSorted!(cmp));
+ static assert(isSorted!(cmp, 1));
+ static assert(isSorted!(cmp, 1, 2, 2));
+ static assert(isSorted!(cmp, 1, 2, 2, 4));
+ static assert(isSorted!(cmp, 1, 2, 2, 4, 8));
+ static assert(!isSorted!(cmp, 32, 2, 2, 4, 8));
+ static assert(isSorted!(cmp, 32, 32));
+}
+
+@nogc nothrow pure @safe unittest
+{
+ enum cmp(int x, int y) = x < y;
+ static assert(isSorted!(cmp));
+ static assert(isSorted!(cmp, 1));
+ static assert(isSorted!(cmp, 1, 2, 2));
+ static assert(isSorted!(cmp, 1, 2, 2, 4));
+ static assert(isSorted!(cmp, 1, 2, 2, 4, 8));
+ static assert(!isSorted!(cmp, 32, 2, 2, 4, 8));
+ static assert(isSorted!(cmp, 32, 32));
+}
+
+/**
+ * Params:
+ * T = A template.
+ * Args = The first arguments for $(D_PARAM T).
+ *
+ * Returns: $(D_PARAM T) with $(D_PARAM Args) applied to it as its first
+ * arguments.
+ */
+template ApplyLeft(alias T, Args...)
+{
+ alias ApplyLeft(U...) = T!(Args, U);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias allAreIntegral = ApplyLeft!(allSatisfy, isIntegral);
+ static assert(allAreIntegral!(int, uint));
+ static assert(!allAreIntegral!(int, float, uint));
+}
+
+/**
+ * Params:
+ * T = A template.
+ * Args = The last arguments for $(D_PARAM T).
+ *
+ * Returns: $(D_PARAM T) with $(D_PARAM Args) applied to it as itslast
+ * arguments.
+ */
+template ApplyRight(alias T, Args...)
+{
+ alias ApplyRight(U...) = T!(U, Args);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias intIs = ApplyRight!(allSatisfy, int);
+ static assert(intIs!(isIntegral));
+ static assert(!intIs!(isUnsigned));
+}
+
+/**
+ * Params:
+ * n = The number of times to repeat $(D_PARAM L).
+ * L = The sequence to be repeated.
+ *
+ * Returns: $(D_PARAM L) repeated $(D_PARAM n) times.
+ */
+template Repeat(size_t n, L...)
+if (n > 0)
+{
+ static if (n == 1)
+ {
+ alias Repeat = L;
+ }
+ else
+ {
+ alias Repeat = AliasSeq!(L, Repeat!(n - 1, L));
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Repeat!(1, uint, int) == AliasSeq!(uint, int)));
+ static assert(is(Repeat!(2, uint, int) == AliasSeq!(uint, int, uint, int)));
+ static assert(is(Repeat!(3) == AliasSeq!()));
+}
+
+private template ReplaceOne(L...)
+{
+ static if (L.length == 2)
+ {
+ alias ReplaceOne = AliasSeq!();
+ }
+ else static if (isEqual!(L[0], L[2]))
+ {
+ alias ReplaceOne = AliasSeq!(L[1], L[3 .. $]);
+ }
+ else
+ {
+ alias ReplaceOne = AliasSeq!(L[2], ReplaceOne!(L[0], L[1], L[3 .. $]));
+ }
+}
+
+/**
+ * Replaces the first occurrence of $(D_PARAM T) in $(D_PARAM L) with
+ * $(D_PARAM U).
+ *
+ * Params:
+ * T = The symbol to be replaced.
+ * U = Replacement.
+ * L = List of symbols.
+ *
+ * Returns: $(D_PARAM L) with the first occurrence of $(D_PARAM T) replaced.
+ */
+template Replace(T, U, L...)
+{
+ alias Replace = ReplaceOne!(T, U, L);
+}
+
+/// ditto
+template Replace(alias T, U, L...)
+{
+ alias Replace = ReplaceOne!(T, U, L);
+}
+
+/// ditto
+template Replace(T, alias U, L...)
+{
+ alias Replace = ReplaceOne!(T, U, L);
+}
+
+/// ditto
+template Replace(alias T, alias U, L...)
+{
+ alias Replace = ReplaceOne!(T, U, L);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Replace!(int, uint, int) == AliasSeq!(uint)));
+ static assert(is(Replace!(int, uint, short, int, int, ushort)
+ == AliasSeq!(short, uint, int, ushort)));
+
+ static assert(Replace!(5, 8, 1, 2, 5, 5) == AliasSeq!(1, 2, 8, 5));
+}
+
+private template ReplaceAllImpl(L...)
+{
+ static if (L.length == 2)
+ {
+ alias ReplaceAllImpl = AliasSeq!();
+ }
+ else
+ {
+ private alias Rest = ReplaceAllImpl!(L[0], L[1], L[3 .. $]);
+ static if (isEqual!(L[0], L[2]))
+ {
+ alias ReplaceAllImpl = AliasSeq!(L[1], Rest);
+ }
+ else
+ {
+ alias ReplaceAllImpl = AliasSeq!(L[2], Rest);
+ }
+ }
+}
+
+/**
+ * Replaces all occurrences of $(D_PARAM T) in $(D_PARAM L) with $(D_PARAM U).
+ *
+ * Params:
+ * T = The symbol to be replaced.
+ * U = Replacement.
+ * L = List of symbols.
+ *
+ * Returns: $(D_PARAM L) with all occurrences of $(D_PARAM T) replaced.
+ */
+template ReplaceAll(T, U, L...)
+{
+ alias ReplaceAll = ReplaceAllImpl!(T, U, L);
+}
+
+/// ditto
+template ReplaceAll(alias T, U, L...)
+{
+ alias ReplaceAll = ReplaceAllImpl!(T, U, L);
+}
+
+/// ditto
+template ReplaceAll(T, alias U, L...)
+{
+ alias ReplaceAll = ReplaceAllImpl!(T, U, L);
+}
+
+/// ditto
+template ReplaceAll(alias T, alias U, L...)
+{
+ alias ReplaceAll = ReplaceAllImpl!(T, U, L);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(ReplaceAll!(int, uint, int) == AliasSeq!(uint)));
+ static assert(is(ReplaceAll!(int, uint, short, int, int, ushort)
+ == AliasSeq!(short, uint, uint, ushort)));
+
+ static assert(ReplaceAll!(5, 8, 1, 2, 5, 5) == AliasSeq!(1, 2, 8, 8));
+}
+
+/**
+ * Params:
+ * L = List of symbols.
+ *
+ * Returns: $(D_PARAM L) with elements in reversed order.
+ */
+template Reverse(L...)
+{
+ static if (L.length == 0)
+ {
+ alias Reverse = AliasSeq!();
+ }
+ else
+ {
+ alias Reverse = AliasSeq!(L[$ - 1], Reverse!(L[0 .. $ - 1]));
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Reverse!(byte, short, int) == AliasSeq!(int, short, byte)));
+}
+
+/**
+ * Applies $(D_PARAM F) to all elements of $(D_PARAM T).
+ *
+ * Params:
+ * F = Template predicate.
+ * T = List of symbols.
+ *
+ * Returns: Elements $(D_PARAM T) after applying $(D_PARAM F) to them.
+ */
+template Map(alias F, T...)
+if (__traits(isTemplate, F))
+{
+ static if (T.length == 0)
+ {
+ alias Map = AliasSeq!();
+ }
+ else
+ {
+ alias Map = AliasSeq!(F!(T[0]), Map!(F, T[1 .. $]));
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Map!(Unqual, const int, immutable short)
+ == AliasSeq!(int, short)));
+}
+
+/**
+ * Sorts $(D_PARAM L) in ascending order according to $(D_PARAM cmp).
+ *
+ * $(D_PARAM cmp) can evaluate to:
+ * $(UL
+ * $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
+ * $(D_INLINECODE a[i] < a[i + 1]).)
+ * $(LI $(D_KEYWORD int): a negative number means that
+ * $(D_INLINECODE a[i] < a[i + 1]), a positive number that
+ * $(D_INLINECODE a[i] > a[i + 1]), `0` if they equal.)
+ * )
+ *
+ * Merge sort is used to sort the arguments.
+ *
+ * Params:
+ * cmp = Sorting template predicate.
+ * L = Elements to be sorted.
+ *
+ * Returns: Elements of $(D_PARAM L) in ascending order.
+ *
+ * See_Also: $(LINK2 https://en.wikipedia.org/wiki/Merge_sort, Merge sort).
+ */
+template Sort(alias cmp, L...)
+if (__traits(isTemplate, cmp))
+{
+ private template merge(size_t A, size_t B)
+ {
+ static if (A + B == L.length)
+ {
+ alias merge = AliasSeq!();
+ }
+ else static if (B >= Right.length
+ || (A < Left.length && isLessEqual!(cmp, Left[A], Right[B])))
+ {
+ alias merge = AliasSeq!(Left[A], merge!(A + 1, B));
+ }
+ else
+ {
+ alias merge = AliasSeq!(Right[B], merge!(A, B + 1));
+ }
+ }
+
+ static if (L.length <= 1)
+ {
+ alias Sort = L;
+ }
+ else
+ {
+ private alias Left = Sort!(cmp, L[0 .. $ / 2]);
+ private alias Right = Sort!(cmp, L[$ / 2 .. $]);
+ alias Sort = merge!(0, 0);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ enum cmp(T, U) = T.sizeof < U.sizeof;
+ static assert(is(Sort!(cmp, long, short, byte, int)
+ == AliasSeq!(byte, short, int, long)));
+}
+
+@nogc nothrow pure @safe unittest
+{
+ enum cmp(int T, int U) = T - U;
+ static assert(Sort!(cmp, 5, 17, 9, 12, 2, 10, 14)
+ == AliasSeq!(2, 5, 9, 10, 12, 14, 17));
+}
+
+private enum bool DerivedToFrontCmp(A, B) = is(A : B);
+
+/**
+ * Returns $(D_PARAM L) sorted in such a way that the most derived types come
+ * first.
+ *
+ * Params:
+ * L = Type tuple.
+ *
+ * Returns: Sorted $(D_PARAM L).
+ */
+template DerivedToFront(L...)
+{
+ alias DerivedToFront = Sort!(DerivedToFrontCmp, L);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ class A
+ {
+ }
+ class B : A
+ {
+ }
+ class C : B
+ {
+ }
+ static assert(is(DerivedToFront!(B, A, C) == AliasSeq!(C, B, A)));
+}
+
+/**
+ * Returns the type from the type tuple $(D_PARAM L) that is most derived from
+ * $(D_PARAM T).
+ *
+ * Params:
+ * T = The type to compare to.
+ * L = Type tuple.
+ *
+ * Returns: The type most derived from $(D_PARAM T).
+ */
+template MostDerived(T, L...)
+{
+ static if (L.length == 0)
+ {
+ alias MostDerived = T;
+ }
+ else static if (is(T : L[0]))
+ {
+ alias MostDerived = MostDerived!(T, L[1 .. $]);
+ }
+ else
+ {
+ alias MostDerived = MostDerived!(L[0], L[1 .. $]);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ class A
+ {
+ }
+ class B : A
+ {
+ }
+ class C : B
+ {
+ }
+ static assert(is(MostDerived!(A, C, B) == C));
+}
+
+private template EraseOne(L...)
+if (L.length > 0)
+{
+ static if (L.length == 1)
+ {
+ alias EraseOne = AliasSeq!();
+ }
+ else static if (isEqual!(L[0 .. 2]))
+ {
+ alias EraseOne = AliasSeq!(L[2 .. $]);
+ }
+ else
+ {
+ alias EraseOne = AliasSeq!(L[1], EraseOne!(L[0], L[2 .. $]));
+ }
+}
+
+/**
+ * Removes the first occurrence of $(D_PARAM T) from the alias sequence
+ * $(D_PARAL L).
+ *
+ * Params:
+ * T = The item to be removed.
+ * L = Alias sequence.
+ *
+ * Returns: $(D_PARAM L) with the first occurrence of $(D_PARAM T) removed.
+ */
+template Erase(T, L...)
+{
+ alias Erase = EraseOne!(T, L);
+}
+
+/// ditto
+template Erase(alias T, L...)
+{
+ alias Erase = EraseOne!(T, L);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Erase!(int, short, int, int, uint) == AliasSeq!(short, int, uint)));
+ static assert(is(Erase!(int, short, uint) == AliasSeq!(short, uint)));
+}
+
+private template EraseAllImpl(L...)
+{
+ static if (L.length == 1)
+ {
+ alias EraseAllImpl = AliasSeq!();
+ }
+ else static if (isEqual!(L[0 .. 2]))
+ {
+ alias EraseAllImpl = EraseAllImpl!(L[0], L[2 .. $]);
+ }
+ else
+ {
+ alias EraseAllImpl = AliasSeq!(L[1], EraseAllImpl!(L[0], L[2 .. $]));
+ }
+}
+
+/**
+ * Removes all occurrences of $(D_PARAM T) from the alias sequence $(D_PARAL L).
+ *
+ * Params:
+ * T = The item to be removed.
+ * L = Alias sequence.
+ *
+ * Returns: $(D_PARAM L) with all occurrences of $(D_PARAM T) removed.
+ */
+template EraseAll(T, L...)
+{
+ alias EraseAll = EraseAllImpl!(T, L);
+}
+
+/// ditto
+template EraseAll(alias T, L...)
+{
+ alias EraseAll = EraseAllImpl!(T, L);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(EraseAll!(int, short, int, int, uint) == AliasSeq!(short, uint)));
+ static assert(is(EraseAll!(int, short, uint) == AliasSeq!(short, uint)));
+ static assert(is(EraseAll!(int, int, int) == AliasSeq!()));
+}
+
+/**
+ * Returns an alias sequence which contains only items that satisfy the
+ * condition $(D_PARAM pred).
+ *
+ * Params:
+ * pred = Template predicate.
+ * L = Alias sequence.
+ *
+ * Returns: $(D_PARAM L) filtered so that it contains only items that satisfy
+ * $(D_PARAM pred).
+ */
+template Filter(alias pred, L...)
+{
+ static if (L.length == 0)
+ {
+ alias Filter = AliasSeq!();
+ }
+ else static if (pred!(L[0]))
+ {
+ alias Filter = AliasSeq!(L[0], Filter!(pred, L[1 .. $]));
+ }
+ else
+ {
+ alias Filter = Filter!(pred, L[1 .. $]);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias Given = AliasSeq!(real, int, bool, uint);
+ static assert(is(Filter!(isIntegral, Given) == AliasSeq!(int, uint)));
+}
+
+/**
+ * Removes all duplicates from the alias sequence $(D_PARAM L).
+ *
+ * Params:
+ * L = Alias sequence.
+ *
+ * Returns: $(D_PARAM L) containing only unique items.
+ */
+template NoDuplicates(L...)
+{
+ static if (L.length == 0)
+ {
+ alias NoDuplicates = AliasSeq!();
+ }
+ else
+ {
+ private alias Rest = NoDuplicates!(EraseAll!(L[0], L[1 .. $]));
+ alias NoDuplicates = AliasSeq!(L[0], Rest);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias Given = AliasSeq!(int, uint, int, short, short, uint);
+ static assert(is(NoDuplicates!Given == AliasSeq!(int, uint, short)));
+}
+
+/**
+ * Converts an input range $(D_PARAM range) into an alias sequence.
+ *
+ * Params:
+ * range = Input range.
+ *
+ * Returns: Alias sequence with items from $(D_PARAM range).
+ */
+template aliasSeqOf(alias range)
+{
+ static if (isArray!(typeof(range)))
+ {
+ static if (range.length == 0)
+ {
+ alias aliasSeqOf = AliasSeq!();
+ }
+ else
+ {
+ alias aliasSeqOf = AliasSeq!(range[0], aliasSeqOf!(range[1 .. $]));
+ }
+ }
+ else
+ {
+ ReturnType!(typeof(&range.front))[] toArray(typeof(range) range)
+ {
+ typeof(return) result;
+ foreach (r; range)
+ {
+ result ~= r;
+ }
+ return result;
+ }
+ alias aliasSeqOf = aliasSeqOf!(toArray(range));
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(aliasSeqOf!([0, 1, 2, 3]) == AliasSeq!(0, 1, 2, 3));
+}
+
+/**
+ * Produces a alias sequence consisting of every $(D_PARAM n)th element of
+ * $(D_PARAM Args), starting with the first.
+ *
+ * Params:
+ * n = Step.
+ * Args = The items to stride.
+ *
+ * Returns: Alias sequence of every $(D_PARAM n)th element of $(D_PARAM Args).
+ */
+template Stride(size_t n, Args...)
+if (n > 0)
+{
+ static if (Args.length > n)
+ {
+ alias Stride = AliasSeq!(Args[0], Stride!(n, Args[n .. $]));
+ }
+ else static if (Args.length > 0)
+ {
+ alias Stride = AliasSeq!(Args[0]);
+ }
+ else
+ {
+ alias Stride = AliasSeq!();
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(Stride!(3, 1, 2, 3, 4, 5, 6, 7, 8) == AliasSeq!(1, 4, 7));
+ static assert(Stride!(2, 1, 2, 3) == AliasSeq!(1, 3));
+ static assert(Stride!(2, 1, 2) == AliasSeq!(1));
+ static assert(Stride!(2, 1) == AliasSeq!(1));
+ static assert(Stride!(1, 1, 2, 3) == AliasSeq!(1, 2, 3));
+ static assert(is(Stride!3 == AliasSeq!()));
+}
+
+/**
+ * Aliases itself to $(D_INLINECODE T[0]) if $(D_PARAM cond) is $(D_KEYWORD true),
+ * to $(D_INLINECODE T[1]) if $(D_KEYWORD false).
+ *
+ * Params:
+ * cond = Template predicate.
+ * T = Two arguments.
+ *
+ * Returns: $(D_INLINECODE T[0]) if $(D_PARAM cond) is $(D_KEYWORD true),
+ * $(D_INLINECODE T[1]) otherwise.
+ */
+template Select(bool cond, T...)
+if (T.length == 2)
+{
+ static if (cond)
+ {
+ alias Select = T[0];
+ }
+ else
+ {
+ alias Select = T[1];
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Select!(true, int, float) == int));
+ static assert(is(Select!(false, int, float) == float));
+}
+
+/**
+ * Attaces a numeric index to each element from $(D_PARAM Args).
+ *
+ * $(D_PSYMBOL EnumerateFrom) returns a sequence of tuples ($(D_PSYMBOL Pack)s)
+ * consisting of the index of each element and the element itself.
+ *
+ * Params:
+ * start = Enumeration initial value.
+ * Args = Enumerated sequence.
+ *
+ * See_Also: $(D_PSYMBOL Enumerate).
+ */
+template EnumerateFrom(size_t start, Args...)
+{
+ static if (Args.length == 0)
+ {
+ alias EnumerateFrom = AliasSeq!();
+ }
+ else
+ {
+ alias EnumerateFrom = AliasSeq!(Pack!(start, Args[0]), EnumerateFrom!(start + 1, Args[1 .. $]));
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(EnumerateFrom!(0, int, uint, bool).length == 3);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias Expected = AliasSeq!(Pack!(cast(size_t) 0, int),
+ Pack!(cast(size_t) 1, uint));
+ static assert(is(EnumerateFrom!(0, int, uint) == Expected));
+}
+
+/**
+ * Attaces a numeric index to each element from $(D_PARAM Args).
+ *
+ * $(D_PSYMBOL EnumerateFrom) returns a sequence of tuples ($(D_PSYMBOL Pack)s)
+ * consisting of the index of each element and the element itself.
+ *
+ * Params:
+ * Args = Enumerated sequence.
+ *
+ * See_Also: $(D_PSYMBOL EnumerateFrom).
+ */
+alias Enumerate(Args...) = EnumerateFrom!(0, Args);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias Expected = AliasSeq!(Pack!(cast(size_t) 0, int),
+ Pack!(cast(size_t) 1, uint));
+ static assert(is(Enumerate!(int, uint) == Expected));
+}
diff --git a/meta/source/tanya/meta/package.d b/meta/source/tanya/meta/package.d
new file mode 100644
index 0000000..d93e4fa
--- /dev/null
+++ b/meta/source/tanya/meta/package.d
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Template metaprogramming.
+ *
+ * This package contains utilities to acquire type information at compile-time,
+ * to transform from one type to another. It has also different algorithms for
+ * iterating, searching and modifying template arguments.
+ *
+ * Copyright: Eugene Wissner 2017-2018.
+ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
+ * Mozilla Public License, v. 2.0).
+ * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
+ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/meta/package.d,
+ * tanya/meta/package.d)
+ */
+module tanya.meta;
+
+public import tanya.meta.metafunction;
+public import tanya.meta.trait;
+public import tanya.meta.transform;
diff --git a/meta/source/tanya/meta/trait.d b/meta/source/tanya/meta/trait.d
new file mode 100644
index 0000000..69b97b9
--- /dev/null
+++ b/meta/source/tanya/meta/trait.d
@@ -0,0 +1,3106 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Type traits.
+ *
+ * Templates in this module are used to obtain type information at compile
+ * time.
+ *
+ * Copyright: Eugene Wissner 2017-2018.
+ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
+ * Mozilla Public License, v. 2.0).
+ * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
+ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/meta/trait.d,
+ * tanya/meta/trait.d)
+ */
+module tanya.meta.trait;
+
+import tanya.meta.metafunction;
+import tanya.meta.transform;
+
+/**
+ * Determines whether $(D_PARAM T) is a wide string, i.e. consists of
+ * $(D_KEYWORD dchar).
+ *
+ * The character type of the string can be qualified with $(D_KEYWORD const),
+ * $(D_KEYWORD immutable) or $(D_KEYWORD inout), but an occurrence of
+ * $(D_KEYWORD shared) in the character type results in returning
+ * $(D_KEYWORD false).
+ * The string itself (in contrast to its character type) can have any type
+ * qualifiers.
+ *
+ * Static $(D_KEYWORD char) and $(D_KEYWORD wchar) arrays are not considered
+ * strings.
+ *
+ * Params:
+ * T = A Type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a wide string,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isNarrowString).
+ */
+enum bool isWideString(T) = is(T : const dchar[]) && !isStaticArray!T;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isWideString!(dchar[]));
+ static assert(!isWideString!(char[]));
+ static assert(!isWideString!(wchar[]));
+
+ static assert(isWideString!dstring);
+ static assert(!isWideString!string);
+ static assert(!isWideString!wstring);
+
+ static assert(isWideString!(const dstring));
+ static assert(!isWideString!(const string));
+ static assert(!isWideString!(const wstring));
+
+ static assert(isWideString!(shared dstring));
+ static assert(!isWideString!(shared string));
+ static assert(!isWideString!(shared wstring));
+
+ static assert(isWideString!(const(dchar)[]));
+ static assert(isWideString!(inout(dchar)[]));
+ static assert(!isWideString!(shared(const(dchar))[]));
+ static assert(!isWideString!(shared(dchar)[]));
+ static assert(!isWideString!(dchar[10]));
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a complex type.
+ *
+ * Complex types are:
+ * $(UL
+ * $(LI cfloat)
+ * $(LI ifloat)
+ * $(LI cdouble)
+ * $(LI idouble)
+ * $(LI creal)
+ * $(LI ireal)
+ * )
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a complex type,
+ * $(D_KEYWORD false) otherwise.
+ */
+enum bool isComplex(T) = is(Unqual!(OriginalType!T) == cfloat)
+ || is(Unqual!(OriginalType!T) == ifloat)
+ || is(Unqual!(OriginalType!T) == cdouble)
+ || is(Unqual!(OriginalType!T) == idouble)
+ || is(Unqual!(OriginalType!T) == creal)
+ || is(Unqual!(OriginalType!T) == ireal);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isComplex!cfloat);
+ static assert(isComplex!ifloat);
+ static assert(isComplex!cdouble);
+ static assert(isComplex!idouble);
+ static assert(isComplex!creal);
+ static assert(isComplex!ireal);
+
+ static assert(!isComplex!float);
+ static assert(!isComplex!double);
+ static assert(!isComplex!real);
+}
+
+/*
+ * Tests whether $(D_PARAM T) is an interface.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an interface,
+ * $(D_KEYWORD false) otherwise.
+ */
+private enum bool isInterface(T) = is(T == interface);
+
+/**
+ * Determines whether $(D_PARAM T) is a polymorphic type, i.e. a
+ * $(D_KEYWORD class) or an $(D_KEYWORD interface).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a $(D_KEYWORD class) or an
+ * $(D_KEYWORD interface), $(D_KEYWORD false) otherwise.
+ */
+enum bool isPolymorphicType(T) = is(T == class) || is(T == interface);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ interface I
+ {
+ }
+ static assert(isPolymorphicType!Object);
+ static assert(isPolymorphicType!I);
+ static assert(!isPolymorphicType!short);
+}
+
+/**
+ * Determines whether the type $(D_PARAM T) has a static method
+ * named $(D_PARAM member).
+ *
+ * Params:
+ * T = Aggregate type.
+ * member = Symbol name.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM member) is a static method of
+ * $(D_PARAM T), $(D_KEYWORD false) otherwise.
+ */
+template hasStaticMember(T, string member)
+{
+ static if (hasMember!(T, member))
+ {
+ alias Member = Alias!(__traits(getMember, T, member));
+
+ static if (__traits(isStaticFunction, Member)
+ || (!isFunction!Member && is(typeof(&Member))))
+ {
+ enum bool hasStaticMember = true;
+ }
+ else
+ {
+ enum bool hasStaticMember = false;
+ }
+ }
+ else
+ {
+ enum bool hasStaticMember = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct S
+ {
+ int member1;
+ void member2()
+ {
+ }
+ static int member3;
+ static void member4()
+ {
+ }
+ static void function() member5;
+ }
+ static assert(!hasStaticMember!(S, "member1"));
+ static assert(!hasStaticMember!(S, "member2"));
+ static assert(hasStaticMember!(S, "member3"));
+ static assert(hasStaticMember!(S, "member4"));
+ static assert(hasStaticMember!(S, "member5"));
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a floating point type.
+ *
+ * Floating point types are:
+ * $(UL
+ * $(LI float)
+ * $(LI double)
+ * $(LI real)
+ * )
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a floating point type,
+ * $(D_KEYWORD false) otherwise.
+ */
+enum bool isFloatingPoint(T) = is(Unqual!(OriginalType!T) == double)
+ || is(Unqual!(OriginalType!T) == float)
+ || is(Unqual!(OriginalType!T) == real);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isFloatingPoint!float);
+ static assert(isFloatingPoint!double);
+ static assert(isFloatingPoint!real);
+ static assert(isFloatingPoint!(const float));
+ static assert(isFloatingPoint!(shared float));
+ static assert(isFloatingPoint!(shared const float));
+ static assert(!isFloatingPoint!int);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a signed numeric type.
+ *
+ * Signed numeric types are:
+ * $(UL
+ * $(LI byte)
+ * $(LI short)
+ * $(LI int)
+ * $(LI long)
+ * $(LI float)
+ * $(LI double)
+ * $(LI real)
+ * )
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a signed numeric type,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isUnsigned).
+ */
+enum bool isSigned(T) = is(Unqual!(OriginalType!T) == byte)
+ || is(Unqual!(OriginalType!T) == short)
+ || is(Unqual!(OriginalType!T) == int)
+ || is(Unqual!(OriginalType!T) == long)
+ || isFloatingPoint!T;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isSigned!byte);
+ static assert(isSigned!short);
+ static assert(isSigned!int);
+ static assert(isSigned!long);
+ static assert(isSigned!float);
+ static assert(isSigned!double);
+ static assert(isSigned!real);
+
+ static assert(!isSigned!ubyte);
+ static assert(!isSigned!ushort);
+ static assert(!isSigned!uint);
+ static assert(!isSigned!ulong);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is an unsigned numeric type.
+ *
+ * Unsigned numeric types are:
+ * $(UL
+ * $(LI ubyte)
+ * $(LI ushort)
+ * $(LI uint)
+ * $(LI ulong)
+ * )
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an unsigned numeric type,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isSigned).
+ */
+enum bool isUnsigned(T) = is(Unqual!(OriginalType!T) == ubyte)
+ || is(Unqual!(OriginalType!T) == ushort)
+ || is(Unqual!(OriginalType!T) == uint)
+ || is(Unqual!(OriginalType!T) == ulong);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isUnsigned!ubyte);
+ static assert(isUnsigned!ushort);
+ static assert(isUnsigned!uint);
+ static assert(isUnsigned!ulong);
+
+ static assert(!isUnsigned!byte);
+ static assert(!isUnsigned!short);
+ static assert(!isUnsigned!int);
+ static assert(!isUnsigned!long);
+ static assert(!isUnsigned!float);
+ static assert(!isUnsigned!double);
+ static assert(!isUnsigned!real);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is an integral type.
+ *
+ * Integral types are:
+ * $(UL
+ * $(LI ubyte)
+ * $(LI ushort)
+ * $(LI uint)
+ * $(LI ulong)
+ * $(LI byte)
+ * $(LI short)
+ * $(LI int)
+ * $(LI long)
+ * )
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an integral type,
+ * $(D_KEYWORD false) otherwise.
+ */
+enum bool isIntegral(T) = isUnsigned!T
+ || is(Unqual!(OriginalType!T) == byte)
+ || is(Unqual!(OriginalType!T) == short)
+ || is(Unqual!(OriginalType!T) == int)
+ || is(Unqual!(OriginalType!T) == long);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isIntegral!ubyte);
+ static assert(isIntegral!byte);
+ static assert(!isIntegral!float);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a numeric (floating point, integral or
+ * complex) type.
+*
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a numeric type,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isIntegral!T),
+ * $(D_PSYMBOL isFloatingPoint),
+ * $(D_PSYMBOL isComplex).
+ */
+enum bool isNumeric(T) = isIntegral!T || isFloatingPoint!T || isComplex!T;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias F = float;
+ static assert(isNumeric!F);
+ static assert(!isNumeric!bool);
+ static assert(!isNumeric!char);
+ static assert(!isNumeric!wchar);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a boolean type, i.e. $(D_KEYWORD bool).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a boolean type,
+ * $(D_KEYWORD false) otherwise.
+ */
+enum bool isBoolean(T) = is(Unqual!(OriginalType!T) == bool);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isBoolean!bool);
+ static assert(isBoolean!(shared const bool));
+ static assert(!isBoolean!(ubyte));
+ static assert(!isBoolean!(byte));
+
+ enum E : bool
+ {
+ t = true,
+ f = false,
+ }
+ static assert(isBoolean!E);
+
+ static struct S1
+ {
+ bool b;
+ alias b this;
+ }
+ static assert(!isBoolean!S1);
+
+ static struct S2
+ {
+ bool opCast(T : bool)()
+ {
+ return true;
+ }
+ }
+ static assert(!isBoolean!S2);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a character type.
+ *
+ * Character types are:
+ *
+ * $(UL
+ * $(LI char)
+ * $(LI wchar)
+ * $(LI dchar)
+ * )
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a character type,
+ * $(D_KEYWORD false) otherwise.
+ */
+enum bool isSomeChar(T) = is(Unqual!(OriginalType!T) == char)
+ || is(Unqual!(OriginalType!T) == wchar)
+ || is(Unqual!(OriginalType!T) == dchar);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isSomeChar!char);
+ static assert(isSomeChar!wchar);
+ static assert(isSomeChar!dchar);
+
+ static assert(!isSomeChar!byte);
+ static assert(!isSomeChar!ubyte);
+ static assert(!isSomeChar!short);
+ static assert(!isSomeChar!ushort);
+ static assert(!isSomeChar!int);
+ static assert(!isSomeChar!uint);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a scalar type.
+ *
+ * Scalar types are numbers, booleans and characters.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a scalar type,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isNumeric),
+ * $(D_PSYMBOL isBoolean),
+ * $(D_PSYMBOL isSomeChar).
+ */
+enum bool isScalarType(T) = isNumeric!T || isBoolean!T || isSomeChar!T;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isScalarType!int);
+ static assert(!isScalarType!(int[]));
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a basic type.
+ *
+ * Basic types are scalar types and $(D_KEYWORD void).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a basic type,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isScalarType).
+ */
+enum bool isBasicType(T) = isScalarType!T || is(T : void);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct S
+ {
+ }
+ class C
+ {
+ }
+ enum E : int
+ {
+ i = 0,
+ }
+
+ static assert(isBasicType!void);
+ static assert(isBasicType!(shared void));
+ static assert(isBasicType!E);
+ static assert(!isBasicType!(int*));
+ static assert(!isBasicType!(void function()));
+ static assert(!isBasicType!C);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a pointer type.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a pointer type,
+ * $(D_KEYWORD false) otherwise.
+ */
+template isPointer(T)
+{
+ static if (is(T U : U*))
+ {
+ enum bool isPointer = !is(Unqual!(OriginalType!T) == typeof(null));
+ }
+ else
+ {
+ enum bool isPointer = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isPointer!(bool*));
+ static assert(isPointer!(const bool*));
+ static assert(isPointer!(const shared bool*));
+ static assert(!isPointer!bool);
+}
+
+// typeof(null) is not a pointer.
+@nogc nothrow pure @safe unittest
+{
+ static assert(!isPointer!(typeof(null)));
+ static assert(!isPointer!(const shared typeof(null)));
+
+ enum typeOfNull : typeof(null)
+ {
+ null_ = null,
+ }
+ static assert(!isPointer!typeOfNull);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is an array type (dynamic or static, but
+ * not an associative one).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an array type,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isAssociativeArray).
+ */
+template isArray(T)
+{
+ static if (is(T U : U[]))
+ {
+ enum bool isArray = true;
+ }
+ else
+ {
+ enum bool isArray = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isArray!(bool[]));
+ static assert(isArray!(const bool[]));
+ static assert(isArray!(shared bool[]));
+ static assert(isArray!(bool[8]));
+ static assert(!isArray!bool);
+ static assert(!isArray!(bool[string]));
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a static array type.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a static array type,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isArray).
+ */
+template isStaticArray(T)
+{
+ static if (is(T U : U[L], size_t L))
+ {
+ enum bool isStaticArray = true;
+ }
+ else
+ {
+ enum bool isStaticArray = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isStaticArray!(bool[8]));
+ static assert(isStaticArray!(const bool[8]));
+ static assert(isStaticArray!(shared bool[8]));
+ static assert(!isStaticArray!(bool[]));
+ static assert(!isStaticArray!bool);
+ static assert(!isStaticArray!(bool[string]));
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a dynamic array type.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a dynamic array type,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isArray).
+ */
+enum bool isDynamicArray(T) = isArray!T && !isStaticArray!T;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isDynamicArray!(bool[]));
+ static assert(isDynamicArray!(const bool[]));
+ static assert(isDynamicArray!(shared bool[]));
+ static assert(!isDynamicArray!(bool[8]));
+ static assert(!isDynamicArray!bool);
+ static assert(!isDynamicArray!(bool[string]));
+}
+
+/**
+ * Determines whether $(D_PARAM T) is an associative array type.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an associative array type,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isArray).
+ */
+template isAssociativeArray(T)
+{
+ static if (is(T U : U[L], L))
+ {
+ enum bool isAssociativeArray = true;
+ }
+ else
+ {
+ enum bool isAssociativeArray = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isAssociativeArray!(bool[string]));
+ static assert(isAssociativeArray!(const bool[string]));
+ static assert(isAssociativeArray!(shared const bool[string]));
+ static assert(!isAssociativeArray!(bool[]));
+ static assert(!isAssociativeArray!(bool[8]));
+ static assert(!isAssociativeArray!bool);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a built-in type.
+ *
+ * Built-in types are all basic types and arrays.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a built-in type,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isBasicType!T),
+ * $(D_PSYMBOL isArray),
+ * $(D_PSYMBOL isAssociativeArray).
+ */
+enum bool isBuiltinType(T) = isBasicType!T
+ || isArray!T
+ || isAssociativeArray!T;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isBuiltinType!int);
+ static assert(isBuiltinType!(int[]));
+ static assert(isBuiltinType!(int[int]));
+ static assert(!isBuiltinType!(int*));
+}
+
+/**
+ * Determines whether $(D_PARAM T) is an aggregate type.
+ *
+ * Aggregate types are:
+ *
+ * $(UL
+ * $(LI $(D_KEYWORD struct)s)
+ * $(LI $(D_KEYWORD class)es)
+ * $(LI $(D_KEYWORD interface)s)
+ * $(LI $(D_KEYWORD union)s)
+ * )
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an aggregate type,
+ * $(D_KEYWORD false) otherwise.
+ */
+enum bool isAggregateType(T) = is(T == struct)
+ || is(T == class)
+ || is(T == interface)
+ || is(T == union);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct S;
+ class C;
+ interface I;
+ union U;
+ enum E;
+
+ static assert(isAggregateType!S);
+ static assert(isAggregateType!C);
+ static assert(isAggregateType!I);
+ static assert(isAggregateType!U);
+ static assert(!isAggregateType!E);
+ static assert(!isAggregateType!void);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a narrow string, i.e. consists of
+ * $(D_KEYWORD char) or $(D_KEYWORD wchar).
+ *
+ * The character type of the string can be qualified with $(D_KEYWORD const),
+ * $(D_KEYWORD immutable) or $(D_KEYWORD inout), but an occurrence of
+ * $(D_KEYWORD shared) in the character type results in returning
+ * $(D_KEYWORD false).
+ * The string itself (in contrast to its character type) can have any type
+ * qualifiers.
+ *
+ * Static $(D_KEYWORD char) and $(D_KEYWORD wchar) arrays are not considered
+ * strings.
+ *
+ * Params:
+ * T = A Type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a narrow string,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isWideString).
+ */
+enum bool isNarrowString(T) = (is(T : const char[]) || is (T : const wchar[]))
+ && !isStaticArray!T;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isNarrowString!(char[]));
+ static assert(isNarrowString!(wchar[]));
+ static assert(!isNarrowString!(dchar[]));
+
+ static assert(isNarrowString!string);
+ static assert(isNarrowString!wstring);
+ static assert(!isNarrowString!dstring);
+
+ static assert(isNarrowString!(const string));
+ static assert(isNarrowString!(const wstring));
+ static assert(!isNarrowString!(const dstring));
+
+ static assert(isNarrowString!(shared string));
+ static assert(isNarrowString!(shared wstring));
+ static assert(!isNarrowString!(shared dstring));
+
+ static assert(isNarrowString!(const(char)[]));
+ static assert(isNarrowString!(inout(char)[]));
+ static assert(!isNarrowString!(shared(const(char))[]));
+ static assert(!isNarrowString!(shared(char)[]));
+ static assert(!isNarrowString!(char[10]));
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a string, i.e. consists of
+ * $(D_KEYWORD char), $(D_KEYWORD wchar) or $(D_KEYWORD dchar).
+ *
+ * The character type of the string can be qualified with $(D_KEYWORD const),
+ * $(D_KEYWORD immutable) or $(D_KEYWORD inout), but an occurrence of
+ * $(D_KEYWORD shared) in the character type results in returning
+ * $(D_KEYWORD false).
+ * The string itself (in contrast to its character type) can have any type
+ * qualifiers.
+ *
+ * Static character arrays are not considered strings.
+ *
+ * Params:
+ * T = A Type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a string,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isNarrowString), $(D_PSYMBOL isWideString).
+ */
+enum bool isSomeString(T) = isNarrowString!T || isWideString!T;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isSomeString!(dchar[]));
+ static assert(isSomeString!(char[]));
+ static assert(isSomeString!(wchar[]));
+
+ static assert(isSomeString!dstring);
+ static assert(isSomeString!string);
+ static assert(isSomeString!wstring);
+
+ static assert(isSomeString!(const dstring));
+ static assert(isSomeString!(const string));
+ static assert(isSomeString!(const wstring));
+
+ static assert(isSomeString!(shared dstring));
+ static assert(isSomeString!(shared string));
+ static assert(isSomeString!(shared wstring));
+
+ static assert(isSomeString!(const(char)[]));
+ static assert(isSomeString!(inout(char)[]));
+ static assert(!isSomeString!(shared(const(char))[]));
+ static assert(!isSomeString!(shared(char)[]));
+ static assert(!isSomeString!(char[10]));
+}
+
+/**
+ * Returns the minimum value of type $(D_PARAM T). In contrast to
+ * $(D_INLINECODE T.min) this template works with floating point and complex
+ * types as well.
+ *
+ * Params:
+ * T = Integral, boolean, floating point, complex or character type.
+ *
+ * Returns: The minimum value of $(D_PARAM T).
+ *
+ * See_Also: $(D_PSYMBOL isIntegral),
+ * $(D_PSYMBOL isBoolean),
+ * $(D_PSYMBOL isSomeChar),
+ * $(D_PSYMBOL isFloatingPoint),
+ * $(D_PSYMBOL isComplex).
+ */
+template mostNegative(T)
+{
+ static if (isIntegral!T || isBoolean!T || isSomeChar!T)
+ {
+ enum T mostNegative = T.min;
+ }
+ else static if (isFloatingPoint!T || isComplex!T)
+ {
+ enum T mostNegative = -T.max;
+ }
+ else
+ {
+ static assert(false, T.stringof ~ " doesn't have the minimum value");
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(mostNegative!char == char.min);
+ static assert(mostNegative!wchar == wchar.min);
+ static assert(mostNegative!dchar == dchar.min);
+
+ static assert(mostNegative!byte == byte.min);
+ static assert(mostNegative!ubyte == ubyte.min);
+ static assert(mostNegative!bool == bool.min);
+
+ static assert(mostNegative!float == -float.max);
+ static assert(mostNegative!double == -double.max);
+ static assert(mostNegative!real == -real.max);
+
+ static assert(mostNegative!ifloat == -ifloat.max);
+ static assert(mostNegative!cfloat == -cfloat.max);
+}
+
+/**
+ * Determines whether the type $(D_PARAM T) is copyable.
+ *
+ * Only structs can be not copyable if their postblit constructor or the
+ * postblit constructor of one of its fields is disabled, i.e. annotated with
+ * $(D_KEYWORD @disable).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_PARAM true) if $(D_PARAM T) can be copied,
+ * $(D_PARAM false) otherwise.
+ */
+enum bool isCopyable(T) = is(typeof({ T s1 = T.init; T s2 = s1; }));
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct S1
+ {
+ }
+ static struct S2
+ {
+ this(this)
+ {
+ }
+ }
+ static struct S3
+ {
+ @disable this(this);
+ }
+ static struct S4
+ {
+ S3 s;
+ }
+ class C
+ {
+ }
+
+ static assert(isCopyable!S1);
+ static assert(isCopyable!S2);
+ static assert(!isCopyable!S3);
+ static assert(!isCopyable!S4);
+
+ static assert(isCopyable!C);
+ static assert(isCopyable!bool);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is an abstract class.
+ *
+ * Abstract class is a class marked as such or a class that has any abstract
+ * methods or doesn't implement all methods of abstract base classes.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is an abstract class,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isAbstractFunction).
+ */
+enum bool isAbstractClass(T) = __traits(isAbstractClass, T);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ class A
+ {
+ }
+ abstract class B
+ {
+ }
+ class C
+ {
+ abstract void func();
+ }
+ class D : C
+ {
+ }
+ class E : C
+ {
+ override void func()
+ {
+ }
+ }
+ static assert(!isAbstractClass!A);
+ static assert(isAbstractClass!B);
+ static assert(isAbstractClass!C);
+ static assert(isAbstractClass!D);
+ static assert(!isAbstractClass!E);
+}
+
+/**
+ * Checks whether $(D_PARAM T) is a type, same as `is(T)` does.
+ *
+ * Params:
+ * T = A symbol.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a type, $(D_KEYWORD false)
+ * otherwise.
+ */
+enum bool isType(alias T) = is(T);
+
+/// ditto
+enum bool isType(T) = true;
+
+/**
+ * Determines whether $(D_PARAM Args) contains only types.
+ *
+ * Params:
+ * Args = Alias sequence.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM Args) consists only of types,
+ * $(D_KEYWORD false) otherwise.
+ */
+enum bool isTypeTuple(Args...) = allSatisfy!(isType, Args);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isTypeTuple!(int, uint, Object));
+ static assert(isTypeTuple!());
+ static assert(!isTypeTuple!(int, 8, Object));
+ static assert(!isTypeTuple!(5, 8, 2));
+
+ class C
+ {
+ }
+ enum E : bool
+ {
+ t,
+ f,
+ }
+ union U
+ {
+ }
+ static struct T()
+ {
+ }
+
+ static assert(isTypeTuple!C);
+ static assert(isTypeTuple!E);
+ static assert(isTypeTuple!U);
+ static assert(isTypeTuple!void);
+ static assert(isTypeTuple!int);
+ static assert(!isTypeTuple!T);
+ static assert(isTypeTuple!(T!()));
+ static assert(!isTypeTuple!5);
+ static assert(!isTypeTuple!(tanya.meta.trait));
+}
+
+/**
+ * Tells whether $(D_PARAM Args) contains only expressions.
+ *
+ * An expression is determined by applying $(D_KEYWORD typeof) to an argument:
+ *
+ * ---
+ * static if (is(typeof(Args[i])))
+ * {
+ * // Args[i] is an expression.
+ * }
+ * else
+ * {
+ * // Args[i] is not an expression.
+ * }
+ * ---
+ *
+ * Params:
+ * Args = Alias sequence.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM Args) consists only of expressions,
+ * $(D_KEYWORD false) otherwise.
+ */
+template isExpressions(Args...)
+{
+ static if (Args.length == 0)
+ {
+ enum bool isExpressions = true;
+ }
+ else static if (is(typeof(Args[0]) U))
+ {
+ enum bool isExpressions = !is(U == void)
+ && isExpressions!(Args[1 .. $]);
+ }
+ else
+ {
+ enum bool isExpressions = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isExpressions!(5, 8, 2));
+ static assert(isExpressions!());
+ static assert(!isExpressions!(int, uint, Object));
+ static assert(!isExpressions!(int, 8, Object));
+
+ template T(U)
+ {
+ }
+ static assert(!isExpressions!T);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a final class.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a final class,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isFinalFunction).
+ */
+enum bool isFinalClass(T) = __traits(isFinalClass, T);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ final class A
+ {
+ }
+ class B
+ {
+ }
+
+ static assert(isFinalClass!A);
+ static assert(!isFinalClass!B);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is an abstract method.
+ *
+ * Params:
+ * F = A symbol.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM F) is an abstract method,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isAbstractClass).
+ */
+enum bool isAbstractFunction(alias F) = __traits(isAbstractFunction, F);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ class A
+ {
+ void func()
+ {
+ }
+ }
+ class B
+ {
+ abstract void func();
+ }
+ class C : B
+ {
+ override void func()
+ {
+ }
+ }
+ static assert(!isAbstractFunction!(A.func));
+ static assert(isAbstractFunction!(B.func));
+ static assert(!isAbstractFunction!(C.func));
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a final method.
+ *
+ * Params:
+ * F = A symbol.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a final method,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isFinalClass).
+ */
+enum bool isFinalFunction(alias F) = __traits(isFinalFunction, F);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ class A
+ {
+ void virtualFunc()
+ {
+ }
+ final void finalFunc()
+ {
+ }
+ }
+
+ static assert(isFinalFunction!(A.finalFunc));
+ static assert(!isFinalFunction!(A.virtualFunc));
+}
+
+/**
+ * Function pointer is a pointer to a function. So a simple function is not
+ * a function pointer, but getting the address of such function returns a
+ * function pointer.
+ *
+ * A function pointer doesn't save the context pointer, thus cannot have access
+ * to its outer scope.
+ *
+ * Params:
+ * F = A symbol.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a function pointer,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(LINK2 http://dlang.org/spec/function.html#closures,
+ * Delegates, Function Pointers, and Closures).
+ */
+template isFunctionPointer(F...)
+if (F.length == 1)
+{
+ static if ((is(typeof(F[0]) T : T*) && is(T == function))
+ || (is(F[0] T : T*) && is(T == function)))
+ {
+ enum bool isFunctionPointer = true;
+ }
+ else
+ {
+ enum bool isFunctionPointer = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isFunctionPointer!(void function()));
+ static assert(!isFunctionPointer!(void delegate()));
+
+ static assert(isFunctionPointer!(() {}));
+
+ void func()
+ {
+ }
+ static void staticFunc()
+ {
+ }
+ interface I
+ {
+ @property int prop();
+ }
+
+ static assert(!isFunctionPointer!func);
+ static assert(!isFunctionPointer!staticFunc);
+
+ auto functionPointer = &staticFunc;
+ auto dg = &func;
+
+ static assert(isFunctionPointer!functionPointer);
+ static assert(!isFunctionPointer!dg);
+
+ static assert(!isFunctionPointer!(I.prop));
+}
+
+/**
+ * Delegate stores the function pointer and function context.
+ *
+ * Params:
+ * F = A symbol.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a delegate,
+ * $(D_KEYWORD false) delegate.
+ *
+ * See_Also: $(LINK2 http://dlang.org/spec/function.html#closures,
+ * Delegates, Function Pointers, and Closures).
+ */
+template isDelegate(F...)
+if (F.length == 1)
+{
+ static if (is(F[0] == delegate)
+ || is(typeof(F[0]) == delegate))
+ {
+ enum bool isDelegate = true;
+ }
+ else
+ {
+ enum bool isDelegate = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isDelegate!(void delegate()));
+ static assert(!isDelegate!(void function()));
+
+ static assert(!isDelegate!(() {}));
+
+ void func()
+ {
+ }
+ static void staticFunc()
+ {
+ }
+ interface I
+ {
+ @property int prop();
+ }
+
+ static assert(!isDelegate!func);
+ static assert(!isDelegate!staticFunc);
+
+ auto functionPointer = &staticFunc;
+ auto dg = &func;
+
+ static assert(!isDelegate!functionPointer);
+ static assert(isDelegate!dg);
+
+ static assert(!isDelegate!(I.prop));
+}
+
+/**
+ * $(D_PSYMBOL isFunction) returns $(D_KEYWORD true) only for plain functions,
+ * not function pointers or delegates. Use $(D_PSYMBOL isFunctionPointer) or
+ * $(D_PSYMBOL isDelegate) to detect them or $(D_PSYMBOL isSomeFunction)
+ * for detecting a function of any type.
+ *
+ * Params:
+ * F = A symbol.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a function,
+ * $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(LINK2 http://dlang.org/spec/function.html#closures,
+ * Delegates, Function Pointers, and Closures).
+ */
+template isFunction(F...)
+if (F.length == 1)
+{
+ static if (is(F[0] == function)
+ || is(typeof(&F[0]) T == delegate)
+ || (is(typeof(&F[0]) T : T*) && is(T == function)))
+ {
+ enum bool isFunction = true;
+ }
+ else
+ {
+ enum bool isFunction = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(!isFunction!(void function()));
+ static assert(!isFunction!(() {}));
+ static assert(!isFunction!(void delegate()));
+
+ void func()
+ {
+ }
+ static void staticFunc()
+ {
+ }
+ interface I
+ {
+ @property int prop();
+ }
+
+ static assert(isFunction!func);
+ static assert(isFunction!staticFunc);
+
+ auto functionPointer = &staticFunc;
+ auto dg = &func;
+
+ static assert(!isFunction!functionPointer);
+ static assert(!isFunction!dg);
+
+ static assert(isFunction!(I.prop));
+}
+
+/**
+ * Params:
+ * F = A symbol.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM F) is a function, function pointer
+ * or delegate, $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(D_PSYMBOL isFunction),
+ * $(D_PSYMBOL isDelegate),
+ * $(D_PSYMBOL isFunctionPointer).
+ */
+template isSomeFunction(F...)
+if (F.length == 1)
+{
+ enum bool isSomeFunction = isFunctionPointer!F
+ || isFunction!F
+ || isDelegate!F;
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isSomeFunction!(void function()));
+ static assert(isSomeFunction!(() {}));
+ static assert(isSomeFunction!(void delegate()));
+
+ void func()
+ {
+ }
+ static void staticFunc()
+ {
+ }
+
+ static assert(isSomeFunction!func);
+ static assert(isSomeFunction!staticFunc);
+
+ auto functionPointer = &staticFunc;
+ auto dg = &func;
+
+ static assert(isSomeFunction!functionPointer);
+ static assert(isSomeFunction!dg);
+
+ static assert(!isSomeFunction!int);
+}
+
+/**
+ * Params:
+ * F = A symbol.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM F) is callable,
+ * $(D_KEYWORD false) otherwise.
+ */
+template isCallable(F...)
+if (F.length == 1)
+{
+ static if (isSomeFunction!F
+ || (is(typeof(F[0].opCall)) && isFunction!(F[0].opCall)))
+ {
+ enum bool isCallable = true;
+ }
+ else
+ {
+ enum bool isCallable = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct S
+ {
+ void opCall()
+ {
+ }
+ }
+ class C
+ {
+ static void opCall()
+ {
+ }
+ }
+ interface I
+ {
+ }
+ S s;
+
+ static assert(isCallable!s);
+ static assert(isCallable!C);
+ static assert(isCallable!S);
+ static assert(!isCallable!I);
+}
+
+@nogc nothrow pure @safe unittest
+{
+ static struct S
+ {
+ @property int opCall()
+ {
+ return 0;
+ }
+ }
+ S s;
+ static assert(isCallable!S);
+ static assert(isCallable!s);
+}
+
+/**
+ * Determines whether $(D_PARAM T) defines a symbol $(D_PARAM member).
+ *
+ * Params:
+ * T = Aggregate type.
+ * member = Symbol name.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) defines a symbol
+ * $(D_PARAM member), $(D_KEYWORD false) otherwise.
+ */
+enum bool hasMember(T, string member) = __traits(hasMember, T, member);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct S
+ {
+ int member1;
+ void member2()
+ {
+ }
+ static int member3;
+ static void member4()
+ {
+ }
+ }
+ static assert(hasMember!(S, "member1"));
+ static assert(hasMember!(S, "member2"));
+ static assert(hasMember!(S, "member3"));
+ static assert(hasMember!(S, "member4"));
+ static assert(!hasMember!(S, "member6"));
+}
+
+/**
+ * Determines whether $(D_PARAM T) is mutable, i.e. has one of the following
+ * qualifiers or a combination of them:
+ *
+ * $(UL
+ * $(LI $(D_KEYWORD const))
+ * $(LI $(D_KEYWORD immutable))
+ * $(LI $(D_KEYWORD const))
+ * )
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) is mutable,
+ * $(D_KEYWORD false) otherwise.
+ */
+template isMutable(T)
+{
+ static if (is(T U == const U)
+ || is(T U == inout U)
+ || is(T U == inout const U)
+ || is(T U == immutable U)
+ || is(T U == shared const U)
+ || is(T U == shared inout U)
+ || is(T U == shared inout const U))
+ {
+ enum bool isMutable = false;
+ }
+ else
+ {
+ enum bool isMutable = true;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct S
+ {
+ void method()
+ {
+ static assert(isMutable!(typeof(this)));
+ }
+
+ void method() inout
+ {
+ static assert(!isMutable!(typeof(this)));
+ }
+
+ void immMethod() const
+ {
+ static assert(!isMutable!(typeof(this)));
+ }
+ void immMethod() immutable
+ {
+ static assert(!isMutable!(typeof(this)));
+ }
+ }
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a nested type, i.e. $(D_KEYWORD class),
+ * $(D_KEYWORD struct) or $(D_KEYWORD union), which internally stores a context
+ * pointer.
+ *
+ * Params:
+ * T = $(D_KEYWORD class), $(D_KEYWORD struct) or $(D_KEYWORD union) type.
+ *
+ * Returns: $(D_KEYWORD true) if the argument is a nested type which internally
+ * stores a context pointer, $(D_KEYWORD false) otherwise.
+ */
+template isNested(T)
+if (is(T == class) || is(T == struct) || is(T == union))
+{
+ enum bool isNested = __traits(isNested, T);
+}
+
+///
+@nogc pure nothrow @safe unittest
+{
+ static struct S
+ {
+ }
+ static assert(!isNested!S);
+
+ class C
+ {
+ void method()
+ {
+ }
+ }
+ static assert(isNested!C);
+}
+
+/**
+ * Determines whether $(D_PARAM T) is a nested function.
+ *
+ * Params:
+ * F = A function.
+ *
+ * Returns $(D_KEYWORD true) if the $(D_PARAM T) is a nested function,
+ * $(D_KEYWORD false) otherwise.
+ */
+enum bool isNestedFunction(alias F) = __traits(isNested, F);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ void func()
+ {
+ void nestedFunc()
+ {
+ }
+ static assert(isNestedFunction!nestedFunc);
+ }
+}
+
+/**
+ * Determines the type of the callable $(D_PARAM F).
+ *
+ * Params:
+ * F = A function.
+ *
+ * Returns: Type of the function $(D_PARAM F).
+ */
+template FunctionTypeOf(F...)
+if (isCallable!F)
+{
+ static if ((is(typeof(F[0]) T : T*) && is(T == function))
+ || (is(F[0] T : T*) && is(T == function))
+ || is(F[0] T == delegate)
+ || is(typeof(F[0]) T == delegate)
+ || is(F[0] T == function)
+ || is(typeof(&F[0]) T == delegate)
+ || (is(typeof(&F[0]) T : T*) && is(T == function)))
+ {
+ alias FunctionTypeOf = T;
+ }
+ else
+ {
+ alias FunctionTypeOf = FunctionTypeOf!(F[0].opCall);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(FunctionTypeOf!(void function()) == function));
+ static assert(is(FunctionTypeOf!(() {}) == function));
+}
+
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(FunctionTypeOf!(void delegate()) == function));
+
+ static void staticFunc()
+ {
+ }
+ auto functionPointer = &staticFunc;
+ static assert(is(FunctionTypeOf!staticFunc == function));
+ static assert(is(FunctionTypeOf!functionPointer == function));
+
+ void func()
+ {
+ }
+ auto dg = &func;
+ static assert(is(FunctionTypeOf!func == function));
+ static assert(is(FunctionTypeOf!dg == function));
+
+ interface I
+ {
+ @property int prop();
+ }
+ static assert(is(FunctionTypeOf!(I.prop) == function));
+
+ static struct S
+ {
+ void opCall()
+ {
+ }
+ }
+ class C
+ {
+ static void opCall()
+ {
+ }
+ }
+ S s;
+
+ static assert(is(FunctionTypeOf!s == function));
+ static assert(is(FunctionTypeOf!C == function));
+ static assert(is(FunctionTypeOf!S == function));
+}
+
+@nogc nothrow pure @safe unittest
+{
+ static struct S2
+ {
+ @property int opCall()
+ {
+ return 0;
+ }
+ }
+ S2 s2;
+ static assert(is(FunctionTypeOf!S2 == function));
+ static assert(is(FunctionTypeOf!s2 == function));
+}
+
+/**
+ * Determines the return type of the callable $(D_PARAM F).
+ *
+ * Params:
+ * F = A callable object.
+ *
+ * Returns: Return type of $(D_PARAM F).
+ */
+template ReturnType(F...)
+if (isCallable!F)
+{
+ static if (is(FunctionTypeOf!(F[0]) T == return))
+ {
+ alias ReturnType = T;
+ }
+ else
+ {
+ static assert(false, "Argument is not a callable");
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(ReturnType!(int delegate()) == int));
+ static assert(is(ReturnType!(bool function()) == bool));
+}
+
+/**
+ * Determines the template $(D_PARAM T) is an instance of.
+ *
+ * Params:
+ * T = Template instance.
+ *
+ * Returns: Template $(D_PARAM T) is an instance of.
+ */
+alias TemplateOf(alias T : Base!Args, alias Base, Args...) = Base;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct S(T)
+ {
+ }
+ static assert(__traits(isSame, TemplateOf!(S!int), S));
+
+ static void func(T)()
+ {
+ }
+ static assert(__traits(isSame, TemplateOf!(func!int), func));
+
+ template T(U)
+ {
+ }
+ static assert(__traits(isSame, TemplateOf!(T!int), T));
+}
+
+/**
+ * Returns the mangled name of the symbol $(D_PARAM T).
+ *
+ * Params:
+ * T = A symbol.
+ *
+ * Returns: Mangled name of $(D_PARAM T).
+ */
+enum string mangledName(T) = T.mangleof;
+
+///
+enum string mangledName(alias T) = T.mangleof;
+
+/**
+ * Tests whether $(D_PARAM I) is an instance of template $(D_PARAM T).
+ *
+ * Params:
+ * T = Template.
+ * I = Template instance.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM I) is an instance of $(D_PARAM T),
+ * $(D_KEYWORD false) otherwise.
+ */
+enum bool isInstanceOf(alias T, I) = is(I == T!Args, Args...);
+
+template isInstanceOf(alias T, alias I)
+{
+ static if (is(typeof(TemplateOf!I)))
+ {
+ enum bool isInstanceOf = __traits(isSame, TemplateOf!I, T);
+ }
+ else
+ {
+ enum bool isInstanceOf = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct S(T)
+ {
+ }
+ static assert(isInstanceOf!(S, S!int));
+
+ static void func(T)();
+ static assert(isInstanceOf!(func, func!int));
+
+ template T(U)
+ {
+ }
+ static assert(isInstanceOf!(T, T!int));
+}
+
+/**
+ * Checks whether $(D_PARAM From) is implicitly (without explicit
+ * $(D_KEYWORD cast)) to $(D_PARAM To).
+ *
+ * Params:
+ * From = Source type.
+ * To = Conversion target type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM From) is implicitly convertible to
+ * $(D_PARAM To), $(D_KEYWORD false) if not.
+ */
+enum bool isImplicitlyConvertible(From, To) = is(From : To);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isImplicitlyConvertible!(const(byte), byte));
+ static assert(isImplicitlyConvertible!(byte, char));
+ static assert(isImplicitlyConvertible!(byte, short));
+ static assert(!isImplicitlyConvertible!(short, byte));
+ static assert(isImplicitlyConvertible!(string, const(char)[]));
+}
+
+/**
+ * Returns a tuple of base classes and interfaces of $(D_PARAM T).
+ *
+ * $(D_PSYMBOL BaseTypeTuple) returns only classes and interfaces $(D_PARAM T)
+ * directly inherits from, but not the base classes and interfaces of its parents.
+ *
+ * Params:
+ * T = Class or interface type.
+ *
+ * Returns: A tuple of base classes or interfaces of ($D_PARAM T).
+ *
+ * See_Also: $(D_PSYMBOL TransitiveBaseTypeTuple).
+ */
+template BaseTypeTuple(T)
+if (is(T == class) || (is(T == interface)))
+{
+ static if (is(T Tuple == super))
+ {
+ alias BaseTypeTuple = Tuple;
+ }
+ else
+ {
+ static assert(false, "Argument isn't a class or interface");
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ interface I1
+ {
+ }
+ interface I2
+ {
+ }
+ interface I3 : I1, I2
+ {
+ }
+ interface I4
+ {
+ }
+ class A : I3, I4
+ {
+ }
+ static assert(is(BaseTypeTuple!A == AliasSeq!(Object, I3, I4)));
+ static assert(BaseTypeTuple!Object.length == 0);
+}
+
+/**
+ * Returns a tuple of all base classes and interfaces of $(D_PARAM T).
+ *
+ * $(D_PSYMBOL TransitiveBaseTypeTuple) returns first the parent class, then
+ * grandparent and so on. The last class is $(D_PSYMBOL Object). Then the interfaces
+ * follow.
+ *
+ * Params:
+ * T = Class or interface type.
+ *
+ * Returns: A tuple of all base classes and interfaces of ($D_PARAM T).
+ *
+ * See_Also: $(D_PSYMBOL BaseTypeTuple).
+ */
+template TransitiveBaseTypeTuple(T)
+if (is(T == class) || is(T == interface))
+{
+ private template Impl(T...)
+ {
+ static if (T.length == 0)
+ {
+ alias Impl = AliasSeq!();
+ }
+ else
+ {
+ alias Impl = AliasSeq!(BaseTypeTuple!(T[0]),
+ Map!(ImplCopy, BaseTypeTuple!(T[0])));
+ }
+ }
+ private alias ImplCopy = Impl; // To avoid recursive template expansion.
+ private enum bool cmp(A, B) = is(B == interface) && is(A == class);
+
+ alias TransitiveBaseTypeTuple = NoDuplicates!(Sort!(cmp, Impl!T));
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ interface I1
+ {
+ }
+ interface I2 : I1
+ {
+ }
+ class A : I2
+ {
+ }
+ class B : A, I1
+ {
+ }
+ class C : B, I2
+ {
+ }
+ alias Expected = AliasSeq!(B, A, Object, I2, I1);
+ static assert(is(TransitiveBaseTypeTuple!C == Expected));
+
+ static assert(is(TransitiveBaseTypeTuple!Object == AliasSeq!()));
+ static assert(is(TransitiveBaseTypeTuple!I2 == AliasSeq!(I1)));
+}
+
+/**
+ * Returns all the base classes of $(D_PARAM T), the direct parent class comes
+ * first, $(D_PSYMBOL Object) ist the last one.
+ *
+ * The only type that doesn't have any base class is $(D_PSYMBOL Object).
+ *
+ * Params:
+ * T = Class type.
+ *
+ * Returns: Base classes of $(D_PARAM T).
+ */
+template BaseClassesTuple(T)
+if (is(T == class))
+{
+ static if (is(T == Object))
+ {
+ alias BaseClassesTuple = AliasSeq!();
+ }
+ else
+ {
+ private alias Parents = BaseTypeTuple!T;
+ alias BaseClassesTuple = AliasSeq!(Parents[0], BaseClassesTuple!(Parents[0]));
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ interface I1
+ {
+ }
+ interface I2
+ {
+ }
+ class A : I1, I2
+ {
+ }
+ class B : A, I1
+ {
+ }
+ class C : B, I2
+ {
+ }
+ static assert(is(BaseClassesTuple!C == AliasSeq!(B, A, Object)));
+ static assert(BaseClassesTuple!Object.length == 0);
+}
+
+/**
+ * Returns all the interfaces $(D_PARAM T) inherits from.
+ *
+ * Params:
+ * T = Class or interface type.
+ *
+ * Returns: Interfaces $(D_PARAM T) inherits from.
+ */
+template InterfacesTuple(T)
+if (is(T == class) || is(T == interface))
+{
+ alias InterfacesTuple = Filter!(isInterface, TransitiveBaseTypeTuple!T);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ interface I1
+ {
+ }
+ interface I2 : I1
+ {
+ }
+ class A : I2
+ {
+ }
+ class B : A, I1
+ {
+ }
+ class C : B, I2
+ {
+ }
+ static assert(is(InterfacesTuple!C == AliasSeq!(I2, I1)));
+
+ static assert(is(InterfacesTuple!Object == AliasSeq!()));
+ static assert(is(InterfacesTuple!I1 == AliasSeq!()));
+}
+
+/**
+ * Tests whether a value of type $(D_PARAM Rhs) can be assigned to a variable
+ * of type $(D_PARAM Lhs).
+ *
+ * If $(D_PARAM Rhs) isn't specified, $(D_PSYMBOL isAssignable) tests whether a
+ * value of type $(D_PARAM Lhs) can be assigned to a variable of the same type.
+ *
+ * $(D_PSYMBOL isAssignable) tells whether $(D_PARAM Rhs) can be assigned by
+ * value as well by reference.
+ *
+ * Params:
+ * Lhs = Variable type.
+ * Rhs = Expression type.
+ *
+ * Returns: $(D_KEYWORD true) if a value of type $(D_PARAM Rhs) can be assigned
+ * to a variable of type $(D_PARAM Lhs), $(D_KEYWORD false) otherwise.
+ */
+template isAssignable(Lhs, Rhs = Lhs)
+{
+ enum bool isAssignable = is(typeof({
+ Lhs lhs = Lhs.init;
+ Rhs rhs = Rhs.init;
+ lhs = ((inout ref Rhs) => Rhs.init)(rhs);
+ }));
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct S1
+ {
+ @disable this();
+ @disable this(this);
+ }
+ static struct S2
+ {
+ void opAssign(S1 s) pure nothrow @safe @nogc
+ {
+ }
+ }
+ static struct S3
+ {
+ void opAssign(ref S1 s) pure nothrow @safe @nogc
+ {
+ }
+ }
+ static assert(isAssignable!(S2, S1));
+ static assert(!isAssignable!(S3, S1));
+
+ static assert(isAssignable!(const(char)[], string));
+ static assert(!isAssignable!(string, char[]));
+
+ static assert(isAssignable!int);
+ static assert(!isAssignable!(const int, int));
+}
+
+/**
+ * Returns template parameters of $(D_PARAM T).
+ *
+ * Params:
+ * T = Template instance.
+ *
+ * Returns: Template parameters of $(D_PARAM T).
+ */
+alias TemplateArgsOf(alias T : Base!Args, alias Base, Args...) = Args;
+
+///
+@nogc nothrow pure @safe unittest
+{
+ template T(A, B)
+ {
+ }
+ static assert(is(TemplateArgsOf!(T!(int, uint)) == AliasSeq!(int, uint)));
+}
+
+/**
+ * Returns a tuple with parameter types of a function.
+ *
+ * Params:
+ * F = A function.
+ *
+ * Returns: Tuple with parameter types of a function.
+ */
+template Parameters(F...)
+if (isCallable!F)
+{
+ static if (is(FunctionTypeOf!F T == function))
+ {
+ alias Parameters = T;
+ }
+ else
+ {
+ static assert(false, "Function has no parameters");
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ int func(Object, uint[]);
+ static assert(is(Parameters!func == AliasSeq!(Object, uint[])));
+}
+
+/**
+ * Returns a string array with all parameter names of a function.
+ *
+ * If a parameter has no name, an empty string is placed into array.
+ *
+ * Params:
+ * F = A function.
+ *
+ * Returns: Function parameter names.
+ */
+template ParameterIdentifierTuple(F...)
+if (isCallable!F)
+{
+ static if (is(FunctionTypeOf!F Params == __parameters))
+ {
+ string[] Impl()
+ {
+ string[] tuple;
+
+ foreach (k, P; Params)
+ {
+ static if (is(typeof(__traits(identifier, Params[k .. $]))))
+ {
+ tuple ~= __traits(identifier, Params[k .. $]);
+ }
+ else
+ {
+ tuple ~= "";
+ }
+ }
+
+ return tuple;
+ }
+ enum string[] ParameterIdentifierTuple = Impl();
+ }
+ else
+ {
+ static assert(false, "Function has no parameters");
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ int func(ref Object stuff, uint[] = null, scope uint k = 1);
+ alias P = ParameterIdentifierTuple!func;
+ static assert(P[0] == "stuff");
+ static assert(P[1] == "");
+ static assert(P[2] == "k");
+}
+
+/// Attributes can be attached to a function.
+enum FunctionAttribute : uint
+{
+ none = 0x0000,
+ pure_ = 0x0001,
+ nothrow_ = 0x0002,
+ ref_ = 0x0004,
+ property = 0x0008,
+ trusted = 0x0010,
+ safe = 0x0020,
+ nogc = 0x0040,
+ system = 0x0080,
+ const_ = 0x0100,
+ immutable_ = 0x0200,
+ inout_ = 0x0400,
+ shared_ = 0x0800,
+ return_ = 0x1000,
+ scope_ = 0x2000,
+}
+
+/**
+ * Retrieves the attributes of the function $(D_PARAM F).
+ *
+ * The attributes are returned as a bit-mask of
+ * $(D_PSYMBOL FunctionAttribute) values.
+ *
+ * Params: A function.
+ *
+ * Returns: Attributes of the function $(D_PARAM F).
+ *
+ * See_Also: $(D_PSYMBOL FunctionAttribute).
+ */
+template functionAttributes(F...)
+if (isCallable!F)
+{
+ uint Impl()
+ {
+ uint attrs = FunctionAttribute.none;
+ foreach (a; __traits(getFunctionAttributes, F[0]))
+ {
+ static if (a == "const")
+ {
+ attrs |= FunctionAttribute.const_;
+ }
+ else static if (a == "immutable")
+ {
+ attrs |= FunctionAttribute.immutable_;
+ }
+ else static if (a == "inout")
+ {
+ attrs |= FunctionAttribute.inout_;
+ }
+ else static if (a == "@nogc")
+ {
+ attrs |= FunctionAttribute.nogc;
+ }
+ else static if (a == "nothrow")
+ {
+ attrs |= FunctionAttribute.nothrow_;
+ }
+ else static if (a == "@property")
+ {
+ attrs |= FunctionAttribute.property;
+ }
+ else static if (a == "pure")
+ {
+ attrs |= FunctionAttribute.pure_;
+ }
+ else static if (a == "ref")
+ {
+ attrs |= FunctionAttribute.ref_;
+ }
+ else static if (a == "return")
+ {
+ attrs |= FunctionAttribute.return_;
+ }
+ else static if (a == "@safe")
+ {
+ attrs |= FunctionAttribute.safe;
+ }
+ else static if (a == "scope")
+ {
+ attrs |= FunctionAttribute.scope_;
+ }
+ else static if (a == "shared")
+ {
+ attrs |= FunctionAttribute.shared_;
+ }
+ else static if (a == "@system")
+ {
+ attrs |= FunctionAttribute.system;
+ }
+ else static if (a == "@trusted")
+ {
+ attrs |= FunctionAttribute.trusted;
+ }
+ }
+ return attrs;
+ }
+ enum uint functionAttributes = Impl();
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ @property ref int func1() pure nothrow @safe @nogc shared scope;
+ static assert((functionAttributes!func1 & FunctionAttribute.pure_)
+ == FunctionAttribute.pure_);
+ static assert((functionAttributes!func1 & FunctionAttribute.nothrow_)
+ == FunctionAttribute.nothrow_);
+ static assert((functionAttributes!func1 & FunctionAttribute.safe)
+ == FunctionAttribute.safe);
+ static assert((functionAttributes!func1 & FunctionAttribute.nogc)
+ == FunctionAttribute.nogc);
+ static assert((functionAttributes!func1 & FunctionAttribute.shared_)
+ == FunctionAttribute.shared_);
+ static assert((functionAttributes!func1 & FunctionAttribute.ref_)
+ == FunctionAttribute.ref_);
+ static assert((functionAttributes!func1 & FunctionAttribute.property)
+ == FunctionAttribute.property);
+ static assert((functionAttributes!func1 & FunctionAttribute.scope_)
+ == FunctionAttribute.scope_);
+ static assert((functionAttributes!func1 & FunctionAttribute.system) == 0);
+ static assert((functionAttributes!func1 & FunctionAttribute.trusted) == 0);
+ static assert((functionAttributes!func1 & FunctionAttribute.return_) == 0);
+}
+
+/**
+ * Returns a tuple with default values of the parameters to $(D_PARAM F).
+ *
+ * If a parameter doesn't have a default value, $(D_KEYWORD void) is returned.
+ *
+ * Params:
+ * F = A function.
+ *
+ * Returns: Default values of the parameters to $(D_PARAM F).
+ */
+template ParameterDefaults(F...)
+if (isCallable!F)
+{
+ static if (is(FunctionTypeOf!F T == __parameters))
+ {
+ private template GetDefault(size_t i)
+ {
+ static if (i == T.length)
+ {
+ alias GetDefault = AliasSeq!();
+ }
+ else
+ {
+ enum getDefault(T[i .. i + 1] name)
+ {
+ return name[0];
+ }
+ static if (is(typeof(getDefault())))
+ {
+ alias Default = Alias!(getDefault());
+ }
+ else
+ {
+ alias Default = void;
+ }
+ alias GetDefault = AliasSeq!(Default, GetDefault!(i + 1));
+ }
+ }
+
+ alias ParameterDefaults = GetDefault!0;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ void func1(int k, uint b = 5, int[] = [1, 2]);
+ alias Defaults = ParameterDefaults!func1;
+ static assert(is(Defaults[0] == void));
+ static assert(Defaults[1 .. 3] == AliasSeq!(5, [1, 2]));
+}
+
+/**
+ * Determines whether $(D_PARAM T) has an elaborate destructor.
+ *
+ * Only $(D_KEYWORD struct)s and static arrays of $(D_KEYWORD struct)s with the
+ * length greater than`0` can have elaborate destructors, for all other types
+ * $(D_PSYMBOL hasElaborateDestructor) evaluates to $(D_KEYWORD false).
+ *
+ * An elaborate destructor is an explicitly defined destructor or one generated
+ * by the compiler. The compiler generates a destructor for a
+ * $(D_KEYWORD struct) if it has members with an elaborate destructor.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) has an elaborate destructor,
+ * $(D_KEYWORD false) otherwise.
+ */
+template hasElaborateDestructor(T)
+{
+ static if (is(T E : E[L], size_t L))
+ {
+ enum bool hasElaborateDestructor = L > 0 && hasElaborateDestructor!E;
+ }
+ else
+ {
+ enum bool hasElaborateDestructor = is(T == struct)
+ && hasMember!(T, "__xdtor");
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ class C
+ {
+ ~this()
+ {
+ }
+ }
+ static assert(!hasElaborateDestructor!C);
+
+ static struct S
+ {
+ ~this()
+ {
+ }
+ }
+ static struct S1
+ {
+ S s;
+ }
+ static struct S2
+ {
+ }
+ static assert(hasElaborateDestructor!S); // Explicit destructor.
+ static assert(hasElaborateDestructor!S1); // Compiler-generated destructor.
+ static assert(!hasElaborateDestructor!S2); // No destructor.
+
+ static assert(hasElaborateDestructor!(S[1]));
+ static assert(!hasElaborateDestructor!(S[0]));
+}
+
+/**
+ * Determines whether $(D_PARAM T) has an elaborate postblit constructor.
+ *
+ * Only $(D_KEYWORD struct)s and static arrays of $(D_KEYWORD struct)s with the
+ * length greater than`0` can have elaborate postblit constructors, for all
+ * other types $(D_PSYMBOL hasElaborateCopyConstructor) evaluates to
+ * $(D_KEYWORD false).
+ *
+ * An elaborate postblit constructor is an explicitly defined postblit
+ * constructor or one generated by the compiler. The compiler generates a
+ * postblit constructor for a
+ * $(D_KEYWORD struct) if it has members with an elaborate postblit
+ * constructor.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) has an elaborate postblit
+ * constructor, $(D_KEYWORD false) otherwise.
+ */
+template hasElaborateCopyConstructor(T)
+{
+ static if (is(T E : E[L], size_t L))
+ {
+ enum bool hasElaborateCopyConstructor = L > 0
+ && hasElaborateCopyConstructor!E;
+ }
+ else
+ {
+ enum bool hasElaborateCopyConstructor = is(T == struct)
+ && hasMember!(T, "__xpostblit");
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(!hasElaborateCopyConstructor!int);
+
+ static struct S
+ {
+ this(this)
+ {
+ }
+ }
+ static struct S1
+ {
+ S s;
+ }
+ static struct S2
+ {
+ }
+ static assert(hasElaborateCopyConstructor!S); // Explicit destructor.
+ static assert(hasElaborateCopyConstructor!S1); // Compiler-generated destructor.
+ static assert(!hasElaborateCopyConstructor!S2); // No destructor.
+ static assert(hasElaborateCopyConstructor!(S[1]));
+ static assert(!hasElaborateCopyConstructor!(S[0]));
+}
+
+/**
+ * Determines whether $(D_PARAM T) has an elaborate assign.
+ *
+ * Only $(D_KEYWORD struct)s and static arrays of $(D_KEYWORD struct)s with the
+ * length greater than`0` can have an elaborate assign, for all
+ * other types $(D_PSYMBOL hasElaborateAssign) evaluates to $(D_KEYWORD false).
+ *
+ * An elaborate assign is defined with $(D_INLINECODE opAssign(typeof(this)))
+ * or $(D_INLINECODE opAssign(ref typeof(this))). An elaborate assign can be
+ * generated for a $(D_KEYWORD struct) by the compiler if one of the members of
+ * this $(D_KEYWORD struct) has an elaborate assign.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) has an elaborate assign,
+ * $(D_KEYWORD false) otherwise.
+ */
+template hasElaborateAssign(T)
+{
+ static if (is(T E : E[L], size_t L))
+ {
+ enum bool hasElaborateAssign = L > 0 && hasElaborateAssign!E;
+ }
+ else static if (is(T == struct))
+ {
+ private enum bool valueAssign = is(typeof({ T.init.opAssign(T()); }));
+ enum bool hasElaborateAssign = valueAssign || is(typeof({
+ T s;
+ s.opAssign(s);
+ }));
+ }
+ else
+ {
+ enum bool hasElaborateAssign = false;
+ }
+}
+
+@nogc nothrow pure @safe unittest
+{
+ static assert(!hasElaborateAssign!int);
+
+ static struct S1
+ {
+ void opAssign(S1)
+ {
+ }
+ }
+ static struct S2
+ {
+ void opAssign(int)
+ {
+ }
+ }
+ static struct S3
+ {
+ S1 s;
+ alias s this;
+ }
+ static assert(hasElaborateAssign!S1);
+ static assert(!hasElaborateAssign!(const S1));
+ static assert(hasElaborateAssign!(S1[1]));
+ static assert(!hasElaborateAssign!(S1[0]));
+ static assert(!hasElaborateAssign!S2);
+ static assert(hasElaborateAssign!S3);
+
+ static struct S4
+ {
+ void opAssign(S4)
+ {
+ }
+ @disable this(this);
+ }
+ static assert(hasElaborateAssign!S4);
+}
+
+/**
+ * Returns all members of $(D_KEYWORD enum) $(D_PARAM T).
+ *
+ * The members of $(D_PARAM T) are typed as $(D_PARAM T), not as a base type
+ * of the enum.
+ *
+ * $(D_PARAM EnumMembers) returns all members of $(D_PARAM T), also if there
+ * are some duplicates.
+ *
+ * Params:
+ * T = A $(D_KEYWORD enum).
+ *
+ * Returns: All members of $(D_PARAM T).
+ */
+template EnumMembers(T)
+if (is(T == enum))
+{
+ private template getEnumMembers(Args...)
+ {
+ static if (Args.length == 1)
+ {
+ enum T getEnumMembers = __traits(getMember, T, Args[0]);
+ }
+ else
+ {
+ alias getEnumMembers = AliasSeq!(__traits(getMember, T, Args[0]),
+ getEnumMembers!(Args[1 .. $]));
+ }
+ }
+ private alias allMembers = AliasSeq!(__traits(allMembers, T));
+ static if (allMembers.length == 1)
+ {
+ alias EnumMembers = AliasSeq!(__traits(getMember, T, allMembers));
+ }
+ else
+ {
+ alias EnumMembers = getEnumMembers!allMembers;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ enum E : int
+ {
+ one,
+ two,
+ three,
+ }
+ static assert([EnumMembers!E] == [E.one, E.two, E.three]);
+}
+
+// Produces a tuple for an enum with only one member
+@nogc nothrow pure @safe unittest
+{
+ enum E : int
+ {
+ one = 0,
+ }
+ static assert(EnumMembers!E == AliasSeq!0);
+}
+
+/**
+ * Different than $(D_INLINECODE T.alignof), which is the same for all class
+ * types, $(D_PSYMBOL classInstanceOf) determines the alignment of the class
+ * instance and not of its reference.
+ *
+ * Params:
+ * T = A class.
+ *
+ * Returns: Alignment of an instance of the class $(D_PARAM T).
+ */
+template classInstanceAlignment(T)
+if (is(T == class))
+{
+ private enum ptrdiff_t pred(U1, U2) = U1.alignof - U2.alignof;
+ private alias Fields = typeof(T.tupleof);
+ enum size_t classInstanceAlignment = Max!(pred, T, Fields).alignof;
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ class C1
+ {
+ }
+ static assert(classInstanceAlignment!C1 == C1.alignof);
+
+ static struct S
+ {
+ align(8)
+ uint s;
+
+ int i;
+ }
+ class C2
+ {
+ S s;
+ }
+ static assert(classInstanceAlignment!C2 == S.alignof);
+}
+
+/**
+ * Tests whether $(D_INLINECODE pred(T)) can be used as condition in an
+ * $(D_KEYWORD if)-statement or a ternary operator.
+ *
+ * $(D_PARAM pred) is an optional parameter. By default $(D_PSYMBOL ifTestable)
+ * tests whether $(D_PARAM T) itself is usable as condition in an
+ * $(D_KEYWORD if)-statement or a ternary operator, i.e. if it a value of type
+ * $(D_PARAM T) can be converted to a boolean.
+ *
+ * Params:
+ * T = A type.
+ * pred = Function with one argument.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_INLINECODE pred(T)) can be used as
+ * condition in an $(D_KEYWORD if)-statement or a ternary operator.
+ */
+template ifTestable(T, alias pred = a => a)
+{
+ enum bool ifTestable = is(typeof(pred(T.init) ? true : false));
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(ifTestable!int);
+
+ static struct S1
+ {
+ }
+ static assert(!ifTestable!S1);
+
+ static struct S2
+ {
+ bool opCast(T : bool)()
+ {
+ return true;
+ }
+ }
+ static assert(ifTestable!S2);
+}
+
+/**
+ * Returns a compile-time tuple of user-defined attributes (UDA) attached to
+ * $(D_PARAM symbol).
+ *
+ * $(D_PARAM symbol) can be:
+ *
+ * $(DL
+ * $(DT Template)
+ * $(DD The attribute is matched if it is an instance of the template
+ * $(D_PARAM attr).)
+ * $(DT Type)
+ * $(DD The attribute is matched if it its type is $(D_PARAM attr).)
+ * $(DT Expression)
+ * $(DD The attribute is matched if it equals to $(D_PARAM attr).)
+ * )
+ *
+ * If $(D_PARAM attr) isn't given, all user-defined attributes of
+ * $(D_PARAM symbol) are returned.
+ *
+ * Params:
+ * symbol = A symbol.
+ * attr = User-defined attribute.
+ *
+ * Returns: A tuple of user-defined attributes attached to $(D_PARAM symbol)
+ * and matching $(D_PARAM attr).
+ *
+ * See_Also: $(LINK2 https://dlang.org/spec/attribute.html#uda,
+ * User Defined Attributes).
+ */
+template getUDAs(alias symbol, alias attr)
+{
+ private template FindUDA(T...)
+ {
+ static if (T.length == 0)
+ {
+ alias FindUDA = AliasSeq!();
+ }
+ else static if ((isTypeTuple!attr && is(TypeOf!(T[0]) == attr))
+ || (is(typeof(T[0] == attr)) && (T[0] == attr))
+ || isInstanceOf!(attr, TypeOf!(T[0])))
+ {
+ alias FindUDA = AliasSeq!(T[0], FindUDA!(T[1 .. $]));
+ }
+ else
+ {
+ alias FindUDA = FindUDA!(T[1 .. $]);
+ }
+ }
+ alias getUDAs = FindUDA!(__traits(getAttributes, symbol));
+}
+
+///
+alias getUDAs(alias symbol) = AliasSeq!(__traits(getAttributes, symbol));
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct Attr
+ {
+ int i;
+ }
+ @Attr int a;
+ static assert(getUDAs!(a, Attr).length == 1);
+
+ @Attr(8) int b;
+ static assert(getUDAs!(b, Attr).length == 1);
+ static assert(getUDAs!(b, Attr)[0].i == 8);
+ static assert(getUDAs!(b, Attr(8)).length == 1);
+ static assert(getUDAs!(b, Attr(7)).length == 0);
+
+ @("string", 5) int c;
+ static assert(getUDAs!(c, "string").length == 1);
+ static assert(getUDAs!(c, 5).length == 1);
+ static assert(getUDAs!(c, "String").length == 0);
+ static assert(getUDAs!(c, 4).length == 0);
+
+ static struct T(U)
+ {
+ enum U s = 7;
+ U i;
+ }
+ @T!int @T!int(8) int d;
+ static assert(getUDAs!(d, T).length == 2);
+ static assert(getUDAs!(d, T)[0].s == 7);
+ static assert(getUDAs!(d, T)[1].i == 8);
+
+ @T int e;
+ static assert(getUDAs!(e, T).length == 0);
+}
+
+/**
+ * Determines whether $(D_PARAM symbol) has user-defined attribute
+ * $(D_PARAM attr) attached to it.
+ *
+ * Params:
+ * symbol = A symbol.
+ * attr = User-defined attribute.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM symbol) has user-defined attribute
+ * $(D_PARAM attr), $(D_KEYWORD false) otherwise.
+ *
+ * See_Also: $(LINK2 https://dlang.org/spec/attribute.html#uda,
+ * User Defined Attributes).
+ */
+template hasUDA(alias symbol, alias attr)
+{
+ enum bool hasUDA = getUDAs!(symbol, attr).length != 0;
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct Attr1
+ {
+ }
+ static struct Attr2
+ {
+ }
+ @Attr1 int a;
+ static assert(hasUDA!(a, Attr1));
+ static assert(!hasUDA!(a, Attr2));
+}
+
+/**
+ * If $(D_PARAM T) is a type, constructs its default value, otherwise
+ * $(D_PSYMBOL evalUDA) aliases itself to $(D_PARAM T).
+ *
+ * This template is useful when working with UDAs with default parameters,
+ * i.e. if an attribute can be given as `@Attr` or `@Attr("param")`,
+ * $(D_PSYMBOL evalUDA) makes `@Attr()` from `@Attr`, but returns
+ * `@Attr("param")` as is.
+ *
+ * $(D_PARAM T) (or its type if it isn't a type already) should have a default
+ * constructor.
+ *
+ * Params:
+ * T = User Defined Attribute.
+ */
+alias evalUDA(alias T) = T;
+
+/// ditto
+alias evalUDA(T) = Alias!(T());
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct Length
+ {
+ size_t length = 8;
+ }
+ @Length @Length(0) int i;
+ alias uda = AliasSeq!(__traits(getAttributes, i));
+
+ alias attr1 = evalUDA!(uda[0]);
+ alias attr2 = evalUDA!(uda[1]);
+
+ static assert(is(typeof(attr1) == Length));
+ static assert(is(typeof(attr2) == Length));
+
+ static assert(attr1.length == 8);
+ static assert(attr2.length == 0);
+}
+
+/**
+ * Tests whether $(D_PARAM T) is an inner class, i.e. a class nested inside
+ * another class.
+ *
+ * All inner classes get `outer` propery automatically generated, which points
+ * to its parent class, though it can be explicitly defined to be something
+ * different. If $(D_PARAM T) does this, $(D_PSYMBOL isInnerClass)
+ * evaluates to $(D_KEYWORD false).
+ *
+ * Params:
+ * T = Class to be tested.
+ *
+ * Returns $(D_KEYWORD true) if $(D_PARAM T) is an inner class,
+ * $(D_KEYWORD false) otherwise.
+ */
+template isInnerClass(T)
+{
+ static if (is(T == class) && is(typeof(T.outer) == class))
+ {
+ enum bool isInnerClass = !canFind!("outer", __traits(allMembers, T));
+ }
+ else
+ {
+ enum bool isInnerClass = false;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ class A
+ {
+ }
+ class O
+ {
+ class I
+ {
+ }
+ class Fake
+ {
+ bool outer;
+ }
+ }
+ static assert(!isInnerClass!(O));
+ static assert(isInnerClass!(O.I));
+ static assert(!isInnerClass!(O.Fake));
+}
+
+@nogc nothrow pure @safe unittest
+{
+ class RefCountedStore(T)
+ {
+ }
+ static assert(!isInnerClass!(RefCountedStore!int));
+}
+
+/**
+ * Returns the types of all members of $(D_PARAM T).
+ *
+ * If $(D_PARAM T) is a $(D_KEYWORD struct) or $(D_KEYWORD union) or
+ * $(D_KEYWORD class), returns the types of all its fields. It is actually the
+ * same as `T.tupleof`, but the content pointer for the nested type isn't
+ * included.
+ *
+ * If $(D_PARAM T) is neither a $(D_KEYWORD struct) nor $(D_KEYWORD union) nor
+ * $(D_KEYWORD class), $(D_PSYMBOL Fields) returns an $(D_PSYMBOL AliasSeq)
+ * with the single element $(D_PARAM T).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_PARAM T)'s fields.
+ */
+template Fields(T)
+{
+ static if ((is(T == struct) || is(T == union)) && isNested!T)
+ {
+ // The last element of .tupleof of a nested struct or union is "this",
+ // the context pointer, type "void*".
+ alias Fields = typeof(T.tupleof[0 .. $ - 1]);
+ }
+ else static if (is(T == class) || is(T == struct) || is(T == union))
+ {
+ alias Fields = typeof(T.tupleof);
+ }
+ else
+ {
+ alias Fields = AliasSeq!T;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ struct Nested
+ {
+ int i;
+
+ void func()
+ {
+ }
+ }
+ static assert(is(Fields!Nested == AliasSeq!int));
+
+ class C
+ {
+ uint u;
+ }
+ static assert(is(Fields!C == AliasSeq!uint));
+
+ static assert(is(Fields!short == AliasSeq!short));
+}
+
+/**
+ * Determines whether all $(D_PARAM Types) are the same.
+ *
+ * If $(D_PARAM Types) is empty, returns $(D_KEYWORD true).
+ *
+ * Params:
+ * Types = Type sequence.
+ *
+ * Returns: $(D_KEYWORD true) if all $(D_PARAM Types) are the same,
+ * $(D_KEYWORD false) otherwise.
+ */
+template allSameType(Types...)
+{
+ static if (Types.length == 0)
+ {
+ enum bool allSameType = true;
+ }
+ else
+ {
+ private enum bool sameType(T) = is(T == Types[0]);
+
+ enum bool allSameType = allSatisfy!(sameType, Types[1 .. $]);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(allSameType!());
+ static assert(allSameType!int);
+ static assert(allSameType!(int, int, int));
+ static assert(!allSameType!(int, uint, int));
+ static assert(!allSameType!(int, uint, short));
+}
+
+/**
+ * Determines whether values of type $(D_PARAM T) can be compared for equality,
+ * i.e. using `==` or `!=` binary operators.
+ *
+ * Params:
+ * T = Type to test.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) can be compared for equality,
+ * $(D_KEYWORD false) otherwise.
+ */
+enum bool isEqualityComparable(T) = ifTestable!(T, a => a == a);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isEqualityComparable!int);
+}
+
+/**
+ * Determines whether values of type $(D_PARAM T) can be compared for ordering,
+ * i.e. using `>`, `>=`, `<` or `<=` binary operators.
+ *
+ * Params:
+ * T = Type to test.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM T) can be compared for ordering,
+ * $(D_KEYWORD false) otherwise.
+ */
+enum bool isOrderingComparable(T) = ifTestable!(T, a => a > a);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(isOrderingComparable!int);
+}
+
+@nogc nothrow pure @safe unittest
+{
+ static struct DisabledOpEquals
+ {
+ @disable bool opEquals(typeof(this)) @nogc nothrow pure @safe;
+
+ int opCmp(typeof(this)) @nogc nothrow pure @safe
+ {
+ return 0;
+ }
+ }
+ static assert(!isEqualityComparable!DisabledOpEquals);
+ static assert(isOrderingComparable!DisabledOpEquals);
+
+ static struct OpEquals
+ {
+ bool opEquals(typeof(this)) @nogc nothrow pure @safe
+ {
+ return true;
+ }
+ }
+ static assert(isEqualityComparable!OpEquals);
+ static assert(!isOrderingComparable!OpEquals);
+}
diff --git a/meta/source/tanya/meta/transform.d b/meta/source/tanya/meta/transform.d
new file mode 100644
index 0000000..5697e69
--- /dev/null
+++ b/meta/source/tanya/meta/transform.d
@@ -0,0 +1,981 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Type transformations.
+ *
+ * Templates in this module can be used to modify type qualifiers or transform
+ * types. They take some type as argument and return a different type after
+ * perfoming the specified transformation.
+ *
+ * Copyright: Eugene Wissner 2017-2018.
+ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
+ * Mozilla Public License, v. 2.0).
+ * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
+ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/meta/transform.d,
+ * tanya/meta/transform.d)
+ */
+module tanya.meta.transform;
+
+import tanya.meta.metafunction;
+import tanya.meta.trait;
+
+/**
+ * Removes any type qualifiers from $(D_PARAM T).
+ *
+ * Removed qualifiers are:
+ * $(UL
+ * $(LI const)
+ * $(LI immutable)
+ * $(LI inout)
+ * $(LI shared)
+ * )
+ * and combinations of these.
+ *
+ * If the type $(D_PARAM T) doesn't have any qualifieres,
+ * $(D_INLINECODE Unqual!T) becomes an alias for $(D_PARAM T).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_PARAM T) without any type qualifiers.
+ */
+template Unqual(T)
+{
+ static if (is(T U == const U)
+ || is(T U == immutable U)
+ || is(T U == inout U)
+ || is(T U == inout const U)
+ || is(T U == shared U)
+ || is(T U == shared const U)
+ || is(T U == shared inout U)
+ || is(T U == shared inout const U))
+ {
+ alias Unqual = U;
+ }
+ else
+ {
+ alias Unqual = T;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Unqual!bool == bool));
+ static assert(is(Unqual!(immutable bool) == bool));
+ static assert(is(Unqual!(inout bool) == bool));
+ static assert(is(Unqual!(inout const bool) == bool));
+ static assert(is(Unqual!(shared bool) == bool));
+ static assert(is(Unqual!(shared const bool) == bool));
+ static assert(is(Unqual!(shared inout const bool) == bool));
+}
+
+/**
+ * If $(D_PARAM T) is an $(D_KEYWORD enum), $(D_INLINECODE OriginalType!T)
+ * evaluates to the most base type of that $(D_KEYWORD enum) and to
+ * $(D_PARAM T) otherwise.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: Base type of the $(D_KEYWORD enum) $(D_PARAM T) or $(D_PARAM T)
+ * itself.
+ */
+template OriginalType(T)
+{
+ static if (is(T U == enum))
+ {
+ alias OriginalType = OriginalType!U;
+ }
+ else
+ {
+ alias OriginalType = T;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ enum E1 : const(int)
+ {
+ n = 0,
+ }
+ enum E2 : bool
+ {
+ t = true,
+ }
+ enum E3 : E2
+ {
+ t = E2.t,
+ }
+ enum E4 : const(E2)
+ {
+ t = E2.t,
+ }
+
+ static assert(is(OriginalType!E1 == const int));
+ static assert(is(OriginalType!E2 == bool));
+ static assert(is(OriginalType!E3 == bool));
+ static assert(is(OriginalType!E4 == bool));
+ static assert(is(OriginalType!(const E4) == bool));
+}
+
+/**
+ * Copies constness of $(D_PARAM From) to $(D_PARAM To).
+ *
+ * The following type qualifiers affect the constness and hence are copied:
+ * $(UL
+ * $(LI const)
+ * $(LI immutable)
+ * $(LI inout)
+ * $(LI inout const)
+ * )
+ *
+ * Params:
+ * From = Source type.
+ * To = Target type.
+ *
+ * Returns: $(D_PARAM To) with the constness of $(D_PARAM From).
+ *
+ * See_Also: $(D_PSYMBOL CopyTypeQualifiers).
+ */
+template CopyConstness(From, To)
+{
+ static if (is(From T == immutable T))
+ {
+ alias CopyConstness = immutable To;
+ }
+ else static if (is(From T == const T) || is(From T == shared const T))
+ {
+ alias CopyConstness = const To;
+ }
+ else static if (is(From T == inout T) || is(From T == shared inout T))
+ {
+ alias CopyConstness = inout To;
+ }
+ else static if (is(From T == inout const T)
+ || is(From T == shared inout const T))
+ {
+ alias CopyConstness = inout const To;
+ }
+ else
+ {
+ alias CopyConstness = To;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(CopyConstness!(int, char) == char));
+ static assert(is(CopyConstness!(const int, char) == const char));
+ static assert(is(CopyConstness!(immutable int, char) == immutable char));
+ static assert(is(CopyConstness!(inout int, char) == inout char));
+ static assert(is(CopyConstness!(inout const int, char) == inout const char));
+
+ static assert(is(CopyConstness!(shared int, char) == char));
+ static assert(is(CopyConstness!(shared const int, char) == const char));
+ static assert(is(CopyConstness!(shared inout int, char) == inout char));
+ static assert(is(CopyConstness!(shared inout const int, char) == inout const char));
+
+ static assert(is(CopyConstness!(const int, shared char) == shared const char));
+ static assert(is(CopyConstness!(const int, immutable char) == immutable char));
+ static assert(is(CopyConstness!(immutable int, const char) == immutable char));
+}
+
+/**
+ * Copies type qualifiers of $(D_PARAM From) to $(D_PARAM To).
+ *
+ * Type qualifiers copied are:
+ * $(UL
+ * $(LI const)
+ * $(LI immutable)
+ * $(LI inout)
+ * $(LI shared)
+ * )
+ * and combinations of these.
+ *
+ * Params:
+ * From = Source type.
+ * To = Target type.
+ *
+ * Returns: $(D_PARAM To) with the type qualifiers of $(D_PARAM From).
+ *
+ * See_Also: $(D_PSYMBOL CopyConstness).
+ */
+template CopyTypeQualifiers(From, To)
+{
+ static if (is(From T == immutable T))
+ {
+ alias CopyTypeQualifiers = immutable To;
+ }
+ else static if (is(From T == const T))
+ {
+ alias CopyTypeQualifiers = const To;
+ }
+ else static if (is(From T == shared T))
+ {
+ alias CopyTypeQualifiers = shared To;
+ }
+ else static if (is(From T == shared const T))
+ {
+ alias CopyTypeQualifiers = shared const To;
+ }
+ else static if (is(From T == inout T))
+ {
+ alias CopyTypeQualifiers = inout To;
+ }
+ else static if (is(From T == shared inout T))
+ {
+ alias CopyTypeQualifiers = shared inout To;
+ }
+ else static if (is(From T == inout const T))
+ {
+ alias CopyTypeQualifiers = inout const To;
+ }
+ else static if (is(From T == shared inout const T))
+ {
+ alias CopyTypeQualifiers = shared inout const To;
+ }
+ else
+ {
+ alias CopyTypeQualifiers = To;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(CopyTypeQualifiers!(int, char) == char));
+ static assert(is(CopyTypeQualifiers!(const int, char) == const char));
+ static assert(is(CopyTypeQualifiers!(immutable int, char) == immutable char));
+ static assert(is(CopyTypeQualifiers!(inout int, char) == inout char));
+ static assert(is(CopyTypeQualifiers!(inout const int, char) == inout const char));
+
+ static assert(is(CopyTypeQualifiers!(shared int, char) == shared char));
+ static assert(is(CopyTypeQualifiers!(shared const int, char) == shared const char));
+ static assert(is(CopyTypeQualifiers!(shared inout int, char) == shared inout char));
+ static assert(is(CopyTypeQualifiers!(shared inout const int, char) == shared inout const char));
+}
+
+/**
+ * Evaluates to the unsigned counterpart of the integral type $(D_PARAM T) preserving all type qualifiers.
+ * If $(D_PARAM T) is already unsigned, $(D_INLINECODE Unsigned!T) aliases $(D_PARAM T).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: Unsigned counterpart of $(D_PARAM T).
+ *
+ * See_Also: $(D_PSYMBOL isSigned).
+ */
+template Unsigned(T)
+if (isIntegral!T)
+{
+ alias UnqualedType = Unqual!(OriginalType!T);
+ static if (is(UnqualedType == byte))
+ {
+ alias Unsigned = CopyTypeQualifiers!(T, ubyte);
+ }
+ else static if (is(UnqualedType == short))
+ {
+ alias Unsigned = CopyTypeQualifiers!(T, ushort);
+ }
+ else static if (is(UnqualedType == int))
+ {
+ alias Unsigned = CopyTypeQualifiers!(T, uint);
+ }
+ else static if (is(UnqualedType == long))
+ {
+ alias Unsigned = CopyTypeQualifiers!(T, ulong);
+ }
+ else
+ {
+ alias Unsigned = T;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Unsigned!byte == ubyte));
+ static assert(is(Unsigned!short == ushort));
+ static assert(is(Unsigned!int == uint));
+ static assert(is(Unsigned!long == ulong));
+
+ static assert(is(Unsigned!(const byte) == const ubyte));
+ static assert(is(Unsigned!(shared byte) == shared ubyte));
+ static assert(is(Unsigned!(shared const byte) == shared const ubyte));
+
+ static assert(!is(Unsigned!float));
+ static assert(is(Unsigned!ubyte == ubyte));
+}
+
+/**
+ * Evaluates to the signed counterpart of the integral type $(D_PARAM T) preserving all type qualifiers.
+ * If $(D_PARAM T) is already signed, $(D_INLINECODE Signed!T) aliases $(D_PARAM T).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: Signed counterpart of $(D_PARAM T).
+ *
+ * See_Also: $(D_PSYMBOL isUnsigned).
+ */
+template Signed(T)
+if (isIntegral!T)
+{
+ alias UnqualedType = Unqual!(OriginalType!T);
+ static if (is(UnqualedType == ubyte))
+ {
+ alias Signed = CopyTypeQualifiers!(T, byte);
+ }
+ else static if (is(UnqualedType == ushort))
+ {
+ alias Signed = CopyTypeQualifiers!(T, short);
+ }
+ else static if (is(UnqualedType == uint))
+ {
+ alias Signed = CopyTypeQualifiers!(T, int);
+ }
+ else static if (is(UnqualedType == ulong))
+ {
+ alias Signed = CopyTypeQualifiers!(T, long);
+ }
+ else
+ {
+ alias Signed = T;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Signed!ubyte == byte));
+ static assert(is(Signed!ushort == short));
+ static assert(is(Signed!uint == int));
+ static assert(is(Signed!ulong == long));
+
+ static assert(is(Signed!(const ubyte) == const byte));
+ static assert(is(Signed!(shared ubyte) == shared byte));
+ static assert(is(Signed!(shared const ubyte) == shared const byte));
+
+ static assert(!is(Signed!float));
+ static assert(is(Signed!byte == byte));
+}
+
+/**
+ * Retrieves the target type `U` of a pointer `U*`.
+ *
+ * Params:
+ * T = Pointer type.
+ *
+ * Returns: Pointer target type.
+ */
+template PointerTarget(T)
+{
+ static if (is(T U : U*))
+ {
+ alias PointerTarget = U;
+ }
+ else
+ {
+ static assert(T.stringof ~ " isn't a pointer type");
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(PointerTarget!(bool*) == bool));
+ static assert(is(PointerTarget!(const bool*) == const bool));
+ static assert(is(PointerTarget!(const shared bool*) == const shared bool));
+ static assert(!is(PointerTarget!bool));
+}
+
+/**
+ * Params:
+ * T = The type of the associative array.
+ *
+ * Returns: The key type of the associative array $(D_PARAM T).
+ */
+template KeyType(T)
+{
+ static if (is(T V : V[K], K))
+ {
+ alias KeyType = K;
+ }
+ else
+ {
+ static assert(false, T.stringof ~ " isn't an associative array");
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(KeyType!(int[string]) == string));
+ static assert(!is(KeyType!(int[15])));
+}
+
+/**
+ * Params:
+ * T = The type of the associative array.
+ *
+ * Returns: The value type of the associative array $(D_PARAM T).
+ */
+template ValueType(T)
+{
+ static if (is(T V : V[K], K))
+ {
+ alias ValueType = V;
+ }
+ else
+ {
+ static assert(false, T.stringof ~ " isn't an associative array");
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(ValueType!(int[string]) == int));
+ static assert(!is(ValueType!(int[15])));
+}
+
+/**
+ * Params:
+ * T = Scalar type.
+ *
+ * Returns: The type $(D_PARAM T) will promote to.
+ *
+ * See_Also: $(LINK2 https://dlang.org/spec/type.html#integer-promotions,
+ * Integer Promotions).
+ */
+template Promoted(T)
+if (isScalarType!T)
+{
+ alias Promoted = CopyTypeQualifiers!(T, typeof(T.init + T.init));
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Promoted!bool == int));
+ static assert(is(Promoted!byte == int));
+ static assert(is(Promoted!ubyte == int));
+ static assert(is(Promoted!short == int));
+ static assert(is(Promoted!ushort == int));
+ static assert(is(Promoted!char == int));
+ static assert(is(Promoted!wchar == int));
+ static assert(is(Promoted!dchar == uint));
+
+ static assert(is(Promoted!(const bool) == const int));
+ static assert(is(Promoted!(shared bool) == shared int));
+}
+
+/**
+ * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_INLINECODE inout(T)).
+ */
+alias InoutOf(T) = inout(T);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(InoutOf!int == inout int));
+}
+
+/**
+ * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_INLINECODE inout(T)).
+ */
+alias ConstOf(T) = const(T);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(ConstOf!int == const int));
+}
+
+/**
+ * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_INLINECODE inout(T)).
+ */
+alias SharedOf(T) = shared(T);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(SharedOf!int == shared int));
+}
+
+/**
+ * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_INLINECODE inout(T)).
+ */
+alias SharedInoutOf(T) = shared(inout T);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(SharedInoutOf!int == shared inout int));
+}
+
+/**
+ * Adds $(D_KEYWORD shared const) qualifier to the type $(D_PARAM T).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_INLINECODE shared(const T)).
+ */
+alias SharedConstOf(T) = shared(const T);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(SharedConstOf!int == shared const int));
+}
+
+/**
+ * Adds $(D_KEYWORD immutable) qualifier to the type $(D_PARAM T).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_INLINECODE immutable(T)).
+ */
+alias ImmutableOf(T) = immutable(T);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(ImmutableOf!int == immutable int));
+}
+
+/**
+ * Adds $(D_KEYWORD inout const) qualifier to the type $(D_PARAM T).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_INLINECODE inout(const T)).
+ */
+alias InoutConstOf(T) = inout(const T);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(InoutConstOf!int == inout const int));
+}
+
+/**
+ * Adds $(D_KEYWORD shared inout const) qualifier to the type $(D_PARAM T).
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_INLINECODE shared(inout const T)).
+ */
+alias SharedInoutConstOf(T) = shared(inout const T);
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(SharedInoutConstOf!int == shared inout const int));
+}
+
+/**
+ * Returns a template with one argument which applies all qualifiers of
+ * $(D_PARAM T) on its argument if instantiated.
+ *
+ * Params:
+ * T = A type.
+ *
+ * Returns: $(D_INLINECODE shared(inout const T)).
+ */
+template QualifierOf(T)
+{
+ static if (is(T U == const U))
+ {
+ alias QualifierOf = ConstOf;
+ }
+ else static if (is(T U == immutable U))
+ {
+ alias QualifierOf = ImmutableOf;
+ }
+ else static if (is(T U == inout U))
+ {
+ alias QualifierOf = InoutOf;
+ }
+ else static if (is(T U == inout const U))
+ {
+ alias QualifierOf = InoutConstOf;
+ }
+ else static if (is(T U == shared U))
+ {
+ alias QualifierOf = SharedOf;
+ }
+ else static if (is(T U == shared const U))
+ {
+ alias QualifierOf = SharedConstOf;
+ }
+ else static if (is(T U == shared inout U))
+ {
+ alias QualifierOf = SharedInoutOf;
+ }
+ else static if (is(T U == shared inout const U))
+ {
+ alias QualifierOf = SharedInoutConstOf;
+ }
+ else
+ {
+ alias QualifierOf(T) = T;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ alias MutableOf = QualifierOf!int;
+ static assert(is(MutableOf!uint == uint));
+
+ alias ConstOf = QualifierOf!(const int);
+ static assert(is(ConstOf!uint == const uint));
+
+ alias InoutOf = QualifierOf!(inout int);
+ static assert(is(InoutOf!uint == inout uint));
+
+ alias InoutConstOf = QualifierOf!(inout const int);
+ static assert(is(InoutConstOf!uint == inout const uint));
+
+ alias ImmutableOf = QualifierOf!(immutable int);
+ static assert(is(ImmutableOf!uint == immutable uint));
+
+ alias SharedOf = QualifierOf!(shared int);
+ static assert(is(SharedOf!uint == shared uint));
+
+ alias SharedConstOf = QualifierOf!(shared const int);
+ static assert(is(SharedConstOf!uint == shared const uint));
+
+ alias SharedInoutOf = QualifierOf!(shared inout int);
+ static assert(is(SharedInoutOf!uint == shared inout uint));
+
+ alias SharedInoutConstOf = QualifierOf!(shared inout const int);
+ static assert(is(SharedInoutConstOf!uint == shared inout const uint));
+}
+
+/**
+ * Determines the type of $(D_PARAM T). If $(D_PARAM T) is already a type,
+ * $(D_PSYMBOL TypeOf) aliases itself to $(D_PARAM T).
+ *
+ * $(D_PSYMBOL TypeOf) evaluates to $(D_KEYWORD void) for template arguments.
+ *
+ * The symbols that don't have a type and aren't types cannot be used as
+ * arguments to $(D_PSYMBOL TypeOf).
+ *
+ * Params:
+ * T = Expression, type or template.
+ *
+ * Returns: The type of $(D_PARAM T).
+ */
+alias TypeOf(T) = T;
+
+/// ditto
+template TypeOf(alias T)
+if (isExpressions!T || __traits(isTemplate, T))
+{
+ alias TypeOf = typeof(T);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ struct S(T)
+ {
+ }
+ static assert(is(TypeOf!S == void));
+ static assert(is(TypeOf!int == int));
+ static assert(is(TypeOf!true == bool));
+ static assert(!is(TypeOf!(tanya.meta)));
+}
+
+// e.g. returns int for int**.
+private template FinalPointerTarget(T)
+{
+ static if (isPointer!T)
+ {
+ alias FinalPointerTarget = FinalPointerTarget!(PointerTarget!T);
+ }
+ else
+ {
+ alias FinalPointerTarget = T;
+ }
+}
+
+// Returns true if T1 is void* and T2 is some pointer.
+private template voidAndPointer(T1, T2)
+{
+ enum bool voidAndPointer = is(Unqual!(PointerTarget!T1) == void)
+ && isPointer!T2;
+}
+
+// Type returned by the ternary operator.
+private alias TernaryType(T, U) = typeof(true ? T.init : U.init);
+
+/**
+ * Determines the type all $(D_PARAM Args) can be implicitly converted to.
+ *
+ * $(OL
+ * $(LI If one of the arguments is $(D_KEYWORD void), the common type is
+ * $(D_KEYWORD void).)
+ * $(LI The common type of integers with the same sign is the type with a
+ * larger size. Signed and unsigned integers don't have a common type.
+ * Type qualifiers are only preserved if all arguments are the same
+ * type.)
+ * $(LI The common type of floating point numbers is the type with more
+ * precision. Type qualifiers are only preserved if all arguments are
+ * the same type.)
+ * $(LI The common type of polymorphic objects is the next, more generic type
+ * both objects inherit from, e.g. $(D_PSYMBOL Object).)
+ * $(LI `void*` is concerned as a common type of pointers only if one of the
+ * arguments is a void pointer.)
+ * $(LI Other types have a common type only if their pointers have a common
+ * type. It means that for example $(D_KEYWORD bool) and $(D_KEYWORD int)
+ don't have a common type. If the types fullfill this condition, the
+ common type is determined with the ternary operator, i.e.
+ `typeof(true ? T1.init : T2.init)` is evaluated.)
+ * )
+ *
+ * If $(D_PARAM Args) don't have a common type, $(D_PSYMBOL CommonType) is
+ * $(D_KEYWORD void).
+ *
+ * Params:
+ * Args = Type list.
+ *
+ * Returns: Common type for $(D_PARAM Args) or $(D_KEYWORD void) if
+ * $(D_PARAM Args) don't have a common type.
+ */
+template CommonType(Args...)
+if (allSatisfy!(isType, Args))
+{
+ static if (Args.length == 0
+ || is(Unqual!(Args[0]) == void)
+ || is(Unqual!(Args[1]) == void))
+ {
+ alias CommonType = void;
+ }
+ else static if (Args.length == 1)
+ {
+ alias CommonType = Args[0];
+ }
+ else
+ {
+ private alias Pair = Args[0 .. 2];
+ private enum bool sameSigned = allSatisfy!(isIntegral, Pair)
+ && isSigned!(Args[0]) == isSigned!(Args[1]);
+
+ static if (is(Args[0] == Args[1]))
+ {
+ alias CommonType = CommonType!(Args[0], Args[2 .. $]);
+ }
+ else static if (sameSigned || allSatisfy!(isFloatingPoint, Pair))
+ {
+ alias CommonType = CommonType!(Unqual!(Largest!Pair),
+ Args[2 .. $]);
+ }
+ else static if (voidAndPointer!Pair
+ || voidAndPointer!(Args[1], Args[0]))
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=15557.
+ // Determine the qualifiers returned by the ternary operator as if
+ // both pointers were int*. Then copy the qualifiers to void*.
+ alias P1 = CopyTypeQualifiers!(FinalPointerTarget!(Args[0]), int)*;
+ alias P2 = CopyTypeQualifiers!(FinalPointerTarget!(Args[1]), int)*;
+ static if (is(TernaryType!(P1, P2) U))
+ {
+ alias CommonType = CopyTypeQualifiers!(PointerTarget!U, void)*;
+ }
+ else
+ {
+ alias CommonType = void;
+ }
+ }
+ else static if ((isPointer!(Args[0]) || isPolymorphicType!(Args[0]))
+ && is(TernaryType!Pair U))
+ {
+ alias CommonType = CommonType!(U, Args[2 .. $]);
+ }
+ else static if (is(TernaryType!(Args[0]*, Args[1]*)))
+ {
+ alias CommonType = CommonType!(TernaryType!Pair, Args[2 .. $]);
+ }
+ else
+ {
+ alias CommonType = void;
+ }
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(CommonType!(int, int, int) == int));
+ static assert(is(CommonType!(ubyte, ushort, uint) == uint));
+ static assert(is(CommonType!(int, uint) == void));
+
+ static assert(is(CommonType!(int, const int) == int));
+ static assert(is(CommonType!(const int, const int) == const int));
+
+ static assert(is(CommonType!(int[], const(int)[]) == const(int)[]));
+ static assert(is(CommonType!(string, char[]) == const(char)[]));
+
+ class A
+ {
+ }
+ static assert(is(CommonType!(const A, Object) == const Object));
+}
+
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(CommonType!(void*, int*) == void*));
+ static assert(is(CommonType!(void*, const(int)*) == const(void)*));
+ static assert(is(CommonType!(void*, const(void)*) == const(void)*));
+ static assert(is(CommonType!(int*, void*) == void*));
+ static assert(is(CommonType!(const(int)*, void*) == const(void)*));
+ static assert(is(CommonType!(const(void)*, void*) == const(void)*));
+
+ static assert(is(CommonType!() == void));
+ static assert(is(CommonType!(int*, const(int)*) == const(int)*));
+ static assert(is(CommonType!(int**, const(int)**) == const(int*)*));
+
+ static assert(is(CommonType!(float, double) == double));
+ static assert(is(CommonType!(float, int) == void));
+
+ static assert(is(CommonType!(bool, const bool) == bool));
+ static assert(is(CommonType!(int, bool) == void));
+ static assert(is(CommonType!(int, void) == void));
+ static assert(is(CommonType!(Object, void*) == void));
+
+ class A
+ {
+ }
+ static assert(is(CommonType!(A, Object) == Object));
+ static assert(is(CommonType!(const(A)*, Object*) == const(Object)*));
+ static assert(is(CommonType!(A, typeof(null)) == A));
+
+ class B : A
+ {
+ }
+ class C : A
+ {
+ }
+ static assert(is(CommonType!(B, C) == A));
+
+ static struct S
+ {
+ int opCast(T : int)()
+ {
+ return 1;
+ }
+ }
+ static assert(is(CommonType!(S, int) == void));
+ static assert(is(CommonType!(const S, S) == const S));
+}
+
+/**
+ * Finds the type with the smallest size in the $(D_PARAM Args) list. If
+ * several types have the same type, the leftmost is returned.
+ *
+ * Params:
+ * Args = Type list.
+ *
+ * Returns: The smallest type.
+ *
+ * See_Also: $(D_PSYMBOL Largest).
+ */
+template Smallest(Args...)
+if (Args.length >= 1)
+{
+ static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
+
+ static if (Args.length == 1)
+ {
+ alias Smallest = Args[0];
+ }
+ else static if (Smallest!(Args[1 .. $]).sizeof < Args[0].sizeof)
+ {
+ alias Smallest = Smallest!(Args[1 .. $]);
+ }
+ else
+ {
+ alias Smallest = Args[0];
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Smallest!(int, ushort, uint, short) == ushort));
+ static assert(is(Smallest!(short) == short));
+ static assert(is(Smallest!(ubyte[8], ubyte[5]) == ubyte[5]));
+ static assert(!is(Smallest!(short, 5)));
+}
+
+/**
+ * Finds the type with the largest size in the $(D_PARAM Args) list. If several
+ * types have the same type, the leftmost is returned.
+ *
+ * Params:
+ * Args = Type list.
+ *
+ * Returns: The largest type.
+ *
+ * See_Also: $(D_PSYMBOL Smallest).
+ */
+template Largest(Args...)
+if (Args.length >= 1)
+{
+ static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
+
+ static if (Args.length == 1)
+ {
+ alias Largest = Args[0];
+ }
+ else static if (Largest!(Args[1 .. $]).sizeof > Args[0].sizeof)
+ {
+ alias Largest = Largest!(Args[1 .. $]);
+ }
+ else
+ {
+ alias Largest = Args[0];
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(Largest!(int, short, uint) == int));
+ static assert(is(Largest!(short) == short));
+ static assert(is(Largest!(ubyte[8], ubyte[5]) == ubyte[8]));
+ static assert(!is(Largest!(short, 5)));
+}