From 666d59c231f2aea29108db07c981664329a314e8 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 22 Aug 2017 11:12:41 +0200 Subject: [PATCH] Add traits for checking if class, iface, struct They are useful for compile-time algorithms like Filter, StaticMap and so on. --- source/tanya/meta/metafunction.d | 319 +++++++++++++++++++-- source/tanya/meta/trait.d | 477 ++++++++++++++++++++++++++----- source/tanya/meta/transform.d | 144 ++++++++++ 3 files changed, 848 insertions(+), 92 deletions(-) diff --git a/source/tanya/meta/metafunction.d b/source/tanya/meta/metafunction.d index 6c46acf..0466a12 100644 --- a/source/tanya/meta/metafunction.d +++ b/source/tanya/meta/metafunction.d @@ -15,42 +15,264 @@ */ module tanya.meta.metafunction; -import tanya.meta.trait; +import tanya.meta.transform; version (unittest) { - import tanya.meta.transform; + import tanya.meta.trait; } -// Used for sorting. -private template lessEqual(alias cmp, Args...) +/** + * 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) { - enum result = cmp!(Args[1], Args[0]); + private enum result = cmp!(Args[1], Args[0]); static if (is(typeof(result) == bool)) { - enum bool lessEqual = !result; + enum bool isLessEqual = !result; } else { - enum bool lessEqual = result >= 0; + enum bool isLessEqual = result >= 0; } } -private template equalTo(Args...) +/// +pure nothrow @safe @nogc 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) +{ + private enum result = cmp!Args; + static if (is(typeof(result) == bool)) + { + enum bool isGreaterEqual = !result; + } + else + { + enum bool isGreaterEqual = result >= 0; + } +} + +/// +pure nothrow @safe @nogc 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) +{ + private enum result = cmp!Args; + static if (is(typeof(result) == bool)) + { + enum bool isLess = result; + } + else + { + enum bool isLess = result < 0; + } +} + +/// +pure nothrow @safe @nogc 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) +{ + 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; + } +} + +/// +pure nothrow @safe @nogc 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. + * + * 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])) || is(Args[0] == Args[1])) { - enum bool equalTo = true; + enum bool isEqual = true; } else { - enum bool equalTo = false; + enum bool isEqual = false; } } +/// +pure nothrow @safe @nogc unittest +{ + static assert(isEqual!(int, 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; +} + +/// +pure nothrow @safe @nogc unittest +{ + static assert(!isNotEqual!(int, int)); + static assert(isNotEqual!(5, int)); + static assert(isNotEqual!(5, 8)); +} + /** * Creates an alias for $(D_PARAM T). * @@ -196,7 +418,7 @@ if (Args.length > 0) { enum ptrdiff_t indexOf = -1; } - else static if (equalTo!(Args[0 .. 2])) + else static if (isEqual!(Args[0 .. 2])) { enum ptrdiff_t indexOf = i; } @@ -391,7 +613,7 @@ template staticIsSorted(alias cmp, L...) else { // `L` is sorted if the both halves and the boundary values are sorted. - enum bool staticIsSorted = lessEqual!(cmp, L[$ / 2 - 1], L[$ / 2]) + enum bool staticIsSorted = isLessEqual!(cmp, L[$ / 2 - 1], L[$ / 2]) && staticIsSorted!(cmp, L[0 .. $ / 2]) && staticIsSorted!(cmp, L[$ / 2 .. $]); } @@ -507,7 +729,7 @@ private template ReplaceOne(L...) { alias ReplaceOne = AliasSeq!(); } - else static if (equalTo!(L[0], L[2])) + else static if (isEqual!(L[0], L[2])) { alias ReplaceOne = AliasSeq!(L[1], L[3 .. $]); } @@ -570,7 +792,7 @@ private template ReplaceAllImpl(L...) else { private alias Rest = ReplaceAllImpl!(L[0], L[1], L[3 .. $]); - static if (equalTo!(L[0], L[2])) + static if (isEqual!(L[0], L[2])) { alias ReplaceAllImpl = AliasSeq!(L[1], Rest); } @@ -707,7 +929,7 @@ template staticSort(alias cmp, L...) alias merge = AliasSeq!(); } else static if (B >= Right.length - || (A < Left.length && lessEqual!(cmp, Left[A], Right[B]))) + || (A < Left.length && isLessEqual!(cmp, Left[A], Right[B]))) { alias merge = AliasSeq!(Left[A], merge!(A + 1, B)); } @@ -823,7 +1045,7 @@ if (L.length > 0) { alias EraseOne = AliasSeq!(); } - else static if (equalTo!(L[0 .. 2])) + else static if (isEqual!(L[0 .. 2])) { alias EraseOne = AliasSeq!(L[2 .. $]); } @@ -867,7 +1089,7 @@ private template EraseAllImpl(L...) { alias EraseAllImpl = AliasSeq!(); } - else static if (equalTo!(L[0 .. 2])) + else static if (isEqual!(L[0 .. 2])) { alias EraseAllImpl = EraseAllImpl!(L[0], L[2 .. $]); } @@ -1005,8 +1227,67 @@ template aliasSeqOf(alias range) /// pure nothrow @safe @nogc unittest { - foreach (i, e; aliasSeqOf!([0, 1, 2, 3])) + 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) { - static assert(i == e); + alias Stride = AliasSeq!(Args[0], Stride!(n, Args[n .. $])); + } + else static if (Args.length > 0) + { + alias Stride = AliasSeq!(Args[0]); + } + else + { + alias Stride = AliasSeq!(); + } +} + +/// +pure nothrow @safe @nogc 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 (condition) + { + alias Select = L[0]; + } + else + { + alias Select = L[1]; } } diff --git a/source/tanya/meta/trait.d b/source/tanya/meta/trait.d index 7291aae..7bb79e1 100644 --- a/source/tanya/meta/trait.d +++ b/source/tanya/meta/trait.d @@ -1617,110 +1617,441 @@ pure nothrow @safe @nogc unittest } } +deprecated("Use tanya.meta.transform.FunctionTypeOf instead") +alias FunctionTypeOf = tanya.meta.transform.FunctionTypeOf; + +deprecated("Use tanya.meta.transform.ReturnType instead") +alias ReturnType = tanya.meta.transform.ReturnType; + /** - * Params: - * F = A function. + * Returns size of the type $(D_PARAM T). * - * Returns: Type of the function $(D_PARAM F). - */ -template FunctionTypeOf(F...) -if (isCallable!F) + * Params: + * T = A type. + * + * Returns: Size of the type $(D_PARAM T). + */ +enum size_t sizeOf(T) = T.sizeof; + +/// +pure nothrow @safe @nogc unittest { - 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))) + static assert(sizeOf!(bool function()) == size_t.sizeof); + static assert(sizeOf!bool == 1); + static assert(sizeOf!short == 2); + static assert(sizeOf!int == 4); + static assert(sizeOf!long == 8); + static assert(sizeOf!(void[16]) == 16); +} + +/** + * 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_INLINECODE Args[0]) and $(D_INLINECODE Args[1]) are the + * same symbol. + * + * Params: + * Args = Two symbols to be tested. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM Args) are the same symbol, + * $(D_KEYWORD false) otherwise. + */ +template isSame(Args...) +if (Args.length == 2) +{ + enum bool isSame = __traits(isSame, Args[0], Args[1]); +} + +/// +pure nothrow @safe @nogc unittest +{ + static assert(isSame!("string", "string")); + static assert(!isSame!(string, immutable(char)[])); +} + +/** + * 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. + */ +enum bool isTemplate(alias T) = __traits(isTemplate, T); + +/// +pure nothrow @safe @nogc unittest +{ + struct S(T) { - alias FunctionTypeOf = T; + } + static assert(isTemplate!S); + static assert(!isTemplate!(S!int)); +} + +/** + * 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 = isSame!(TemplateOf!I, T); } else { - alias FunctionTypeOf = FunctionTypeOf!(F[0].opCall); + enum bool isInstanceOf = false; } } /// 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() + struct S(T) { } - auto functionPointer = &staticFunc; - static assert(is(FunctionTypeOf!staticFunc == function)); - static assert(is(FunctionTypeOf!functionPointer == function)); + static assert(isInstanceOf!(S, S!int)); - void func() + static void func(T)() { } - auto dg = &func; - static assert(is(FunctionTypeOf!func == function)); - static assert(is(FunctionTypeOf!dg == function)); + static assert(isInstanceOf!(func, func!int)); - interface I + template T(U) { - @property int prop(); } - static assert(is(FunctionTypeOf!(I.prop) == function)); + static assert(isInstanceOf!(T, T!int)); - 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. + * Checks whether $(D_PARAM From) is implicitly (without explicit + * $(D_KEYWORD cast)) to $(D_PARAM To). * - * Returns: Return type of $(D_PARAM F). + * 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. */ -template ReturnType(F...) -if (isCallable!F) +enum bool isImplicitlyConvertible(From, To) = is(From : To); + +/// +pure nothrow @safe @nogc unittest { - static if (is(FunctionTypeOf!(F[0]) T == return)) + 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)[])); +} + +/** + * Determine 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. + */ +enum bool isInterface(T) = is(T == interface); + +/** + * Determine whether $(D_PARAM T) is a class. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a class + * $(D_KEYWORD false) otherwise. + */ +enum bool isClass(T) = is(T == class); + +/** + * Determine whether $(D_PARAM T) is a struct. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a struct, + * $(D_KEYWORD false) otherwise. + */ +enum bool isStruct(T) = is(T == struct); + +/** + * 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 ReturnType = T; + alias BaseTypeTuple = Tuple; } else { - static assert(false, "Argument is not a callable"); + static assert(false, "Argument isn't a class or interface"); } } + +/// +pure nothrow @safe @nogc 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]), + staticMap!(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!(staticSort!(cmp, Impl!T)); +} + +/// +pure nothrow @safe @nogc 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])); + } +} + +/// +pure nothrow @safe @nogc 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); +} + +/// +pure nothrow @safe @nogc 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); + })); +} + +/// +pure nothrow @safe @nogc unittest +{ + struct S1 + { + @disable this(); + @disable this(this); + } + struct S2 + { + void opAssign(S1 s) pure nothrow @safe @nogc + { + } + } + 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)); +} diff --git a/source/tanya/meta/transform.d b/source/tanya/meta/transform.d index ac6a773..82a5fc9 100644 --- a/source/tanya/meta/transform.d +++ b/source/tanya/meta/transform.d @@ -473,3 +473,147 @@ pure nothrow @safe @nogc unittest static assert(is(Promoted!(const bool) == const int)); 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)); +}