diff options
| author | Eugen Wissner <belka@caraus.de> | 2019-03-02 08:01:58 +0100 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2019-03-02 08:08:10 +0100 |
| commit | 5b850d532edebf83d8988ff413cbd2a988011630 (patch) | |
| tree | ed9c996e52939fcf4ea88518cd7cdb000e0869d9 /meta | |
| parent | d7dfa3f6f14309327934fa846aed6a5c3b40dd6a (diff) | |
| download | tanya-5b850d532edebf83d8988ff413cbd2a988011630.tar.gz | |
Move meta into a separate subpackage
Diffstat (limited to 'meta')
| -rw-r--r-- | meta/dub.json | 5 | ||||
| -rw-r--r-- | meta/source/tanya/meta/metafunction.d | 1862 | ||||
| -rw-r--r-- | meta/source/tanya/meta/package.d | 23 | ||||
| -rw-r--r-- | meta/source/tanya/meta/trait.d | 3106 | ||||
| -rw-r--r-- | meta/source/tanya/meta/transform.d | 981 |
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))); +} |
