From 520bd399a3102c243928007ed63ff7e545d32d27 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 5 Sep 2017 05:51:34 +0200 Subject: [PATCH] Add template-time Set and set-theoretic metafunctions --- source/tanya/meta/metafunction.d | 208 ++++++++++++++++++++++++++++--- source/tanya/meta/trait.d | 151 +++++++++++++++++++++- source/tanya/meta/transform.d | 150 +--------------------- 3 files changed, 337 insertions(+), 172 deletions(-) diff --git a/source/tanya/meta/metafunction.d b/source/tanya/meta/metafunction.d index a683589..b619db0 100644 --- a/source/tanya/meta/metafunction.d +++ b/source/tanya/meta/metafunction.d @@ -112,7 +112,7 @@ pure nothrow @safe @nogc unittest } /** - * Zips one or more $(D_PSYMBOL AliasTuple)s with $(D_PARAM f). + * Zips one or more $(D_PSYMBOL Tuple)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) @@ -137,7 +137,7 @@ pure nothrow @safe @nogc unittest * Params: * f = Some template that can be applied to the elements of * $(D_PARAM Tuples). - * Tuples = $(D_PSYMBOL AliasTuple) instances. + * Tuples = $(D_PSYMBOL Tuple) instances. * * Returns: A sequence, whose $(I i)-th element contains the $(I i)-th element * from each of the $(D_PARAM Tuples). @@ -145,7 +145,7 @@ pure nothrow @safe @nogc unittest template ZipWith(alias f, Tuples...) if (Tuples.length > 0 && isTemplate!f - && allSatisfy!(ApplyLeft!(isInstanceOf, AliasTuple), Tuples)) + && allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Tuples)) { private template GetIth(size_t i, Args...) { @@ -179,24 +179,24 @@ if (Tuples.length > 0 pure nothrow @safe @nogc unittest { alias Result1 = ZipWith!(AliasSeq, - AliasTuple!(1, 2), - AliasTuple!(5, 6), - AliasTuple!(9, 10)); + Tuple!(1, 2), + Tuple!(5, 6), + Tuple!(9, 10)); static assert(Result1 == AliasSeq!(1, 5, 9, 2, 6, 10)); alias Result2 = ZipWith!(AliasSeq, - AliasTuple!(1, 2, 3), - AliasTuple!(4, 5)); + Tuple!(1, 2, 3), + Tuple!(4, 5)); static assert(Result2 == AliasSeq!(1, 4, 2, 5)); - alias Result3 = ZipWith!(AliasSeq, AliasTuple!(), AliasTuple!(4, 5)); + alias Result3 = ZipWith!(AliasSeq, Tuple!(), Tuple!(4, 5)); static assert(Result3.length == 0); } /** * Holds a typed sequence of template parameters. * - * Different than $(D_PSYMBOL AliasSeq), $(D_PSYMBOL AliasTuple) doesn't unpack + * Different than $(D_PSYMBOL AliasSeq), $(D_PSYMBOL Tuple) doesn't unpack * its template parameters automatically. Consider: * * --- @@ -211,7 +211,7 @@ pure nothrow @safe @nogc unittest * 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 AliasTuple) it is possible to pass distinguishable + * With $(D_PSYMBOL Tuple) it is possible to pass distinguishable * sequences of parameters to a template. So: * * --- @@ -220,15 +220,15 @@ pure nothrow @safe @nogc unittest * static assert(Args.length == 2); * } * - * alias BInstance = B!(AliasTuple!(int, uint), AliasTuple!(float, double)); + * alias BInstance = B!(Tuple!(int, uint), Tuple!(float, double)); * --- * * Params: - * Args = Elements of this $(D_PSYMBOL AliasTuple). + * Args = Elements of this $(D_PSYMBOL Tuple). * * See_Also: $(D_PSYMBOL AliasSeq). */ -template AliasTuple(Args...) +template Tuple(Args...) { /// Elements in this tuple as $(D_PSYMBOL AliasSeq). alias Seq = Args; @@ -240,9 +240,9 @@ template AliasTuple(Args...) /// pure nothrow @safe @nogc unittest { - alias A = AliasTuple!(short); - alias B = AliasTuple!(3, 8, 9); - alias C = AliasTuple!(A, B); + alias A = Tuple!(short); + alias B = Tuple!(3, 8, 9); + alias C = Tuple!(A, B); static assert(C.length == 2); @@ -251,11 +251,175 @@ pure nothrow @safe @nogc unittest static assert(B.length == 3); static assert(B.Seq == AliasSeq!(3, 8, 9)); - alias D = AliasTuple!(); + alias D = Tuple!(); 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 filteredout, 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 Tuple). + */ +template 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; +} + +/// +pure nothrow @safe @nogc 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); +} + +/// +pure nothrow @safe @nogc 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)); + } +} + +/// +pure nothrow @safe @nogc 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)); + } +} + +/// +pure nothrow @safe @nogc 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). @@ -451,6 +615,9 @@ pure nothrow @safe @nogc unittest * $(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. * @@ -461,7 +628,8 @@ template isEqual(Args...) if (Args.length == 2) { static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1])) - || is(Args[0] == Args[1])) + || (isTypeTuple!Args && is(Args[0] == Args[1])) + || isSame!Args) { enum bool isEqual = true; } @@ -475,6 +643,8 @@ if (Args.length == 2) pure nothrow @safe @nogc 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)); } diff --git a/source/tanya/meta/trait.d b/source/tanya/meta/trait.d index a5e616a..d428ddd 100644 --- a/source/tanya/meta/trait.d +++ b/source/tanya/meta/trait.d @@ -416,6 +416,9 @@ version (TanyaPhobos) EnumMembers, classInstanceAlignment, ifTestable, + FunctionTypeOf, + ReturnType, + TemplateOf, isTypeTuple, isExpressions; } @@ -1873,11 +1876,149 @@ pure nothrow @safe @nogc unittest } } -deprecated("Use tanya.meta.transform.FunctionTypeOf instead") -alias FunctionTypeOf = tanya.meta.transform.FunctionTypeOf; +/** + * 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); + } +} -deprecated("Use tanya.meta.transform.ReturnType instead") -alias ReturnType = tanya.meta.transform.ReturnType; +/// +pure nothrow @safe @nogc unittest +{ + static assert(is(FunctionTypeOf!(void function()) == function)); + static assert(is(FunctionTypeOf!(() {}) == function)); +} + +private pure nothrow @safe @nogc 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)); + + 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)); +} + +private pure nothrow @safe @nogc unittest +{ + struct S2 + { + @property int opCall() + { + return 0; + } + } + S2 s2; + static assert(is(FunctionTypeOf!S2 == function)); + static assert(is(FunctionTypeOf!s2 == function)); +} + +/** + * 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"); + } +} + +/// +pure nothrow @safe @nogc 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; + +/// +pure nothrow @safe @nogc unittest +{ + struct S(T) + { + } + static assert(isSame!(TemplateOf!(S!int), S)); + + static void func(T)() + { + } + static assert(isSame!(TemplateOf!(func!int), func)); + + template T(U) + { + } + static assert(isSame!(TemplateOf!(T!int), T)); +} /** * Returns the mangled name of the symbol $(D_PARAM T). @@ -2392,7 +2533,7 @@ if (isCallable!F) { attrs |= FunctionAttribute.shared_; } - else static if (a == "system") + else static if (a == "@system") { attrs |= FunctionAttribute.system; } diff --git a/source/tanya/meta/transform.d b/source/tanya/meta/transform.d index af30aa9..b72fc59 100644 --- a/source/tanya/meta/transform.d +++ b/source/tanya/meta/transform.d @@ -6,7 +6,8 @@ * Type transformations. * * Templates in this module can be used to modify type qualifiers or transform - * types. + * types. They take some type as argument and return a different type after + * perfoming the specified transformation. * * Copyright: Eugene Wissner 2017. * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, @@ -29,9 +30,6 @@ version (TanyaPhobos) KeyType, ValueType, Promoted, - FunctionTypeOf, - ReturnType, - TemplateOf, InoutOf, ConstOf, SharedOf, @@ -499,150 +497,6 @@ pure nothrow @safe @nogc unittest static assert(is(Promoted!(shared bool) == shared int)); } -/** - * 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); - } -} - -/// -pure nothrow @safe @nogc unittest -{ - static assert(is(FunctionTypeOf!(void function()) == function)); - static assert(is(FunctionTypeOf!(() {}) == function)); -} - -private pure nothrow @safe @nogc 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)); - - 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)); -} - -private pure nothrow @safe @nogc unittest -{ - struct S2 - { - @property int opCall() - { - return 0; - } - } - S2 s2; - static assert(is(FunctionTypeOf!S2 == function)); - static assert(is(FunctionTypeOf!s2 == function)); -} - -/** - * 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"); - } -} - -/// -pure nothrow @safe @nogc 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; - -/// -pure nothrow @safe @nogc unittest -{ - struct S(T) - { - } - static assert(isSame!(TemplateOf!(S!int), S)); - - static void func(T)() - { - } - static assert(isSame!(TemplateOf!(func!int), func)); - - template T(U) - { - } - static assert(isSame!(TemplateOf!(T!int), T)); -} - /** * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T). *