From 9355c541632a1e39a4ef8daefda49f8cc8055ff9 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Fri, 18 Aug 2017 23:38:41 +0200 Subject: [PATCH] Add metafunctions --- source/tanya/meta/metafunction.d | 208 ++++++++++++++++++ source/tanya/meta/package.d | 1 + source/tanya/meta/traits.d | 353 ++++++++++++++++++++++++++++--- 3 files changed, 534 insertions(+), 28 deletions(-) create mode 100644 source/tanya/meta/metafunction.d diff --git a/source/tanya/meta/metafunction.d b/source/tanya/meta/metafunction.d new file mode 100644 index 0000000..75c717c --- /dev/null +++ b/source/tanya/meta/metafunction.d @@ -0,0 +1,208 @@ +/* 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 contains functions that manipulate template type lists as well + * as algorithms to perform arbitrary compile-time computations. + * + * Copyright: Eugene Wissner 2017. + * 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; + +version (unittest) +{ + import std.traits; +} + +/** + * 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; + +/// +pure nothrow @safe @nogc 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))); +} + +/** + * Params: + * Args = List of symbols. + * + * Returns: An alias for sequence $(D_PARAM Args). + * + * See_Also: $(D_PSYMBOL Alias). + */ +alias AliasSeq(Args...) = Args; + +/// +pure nothrow @safe @nogc 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); +} + +/** + * 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...) +{ + 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; + } +} + +/// +pure nothrow @safe @nogc 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...) +{ + 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 .. $]); + } +} + +/// +pure nothrow @safe @nogc 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 (is(Args[0] == Args[1]) + || (is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1]))) + { + 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 type to search for. + * L = Type list. + * + * Returns: The index of the first occurence 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)); +} + +/// +pure nothrow @safe @nogc 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); +} diff --git a/source/tanya/meta/package.d b/source/tanya/meta/package.d index 5002849..4b74287 100644 --- a/source/tanya/meta/package.d +++ b/source/tanya/meta/package.d @@ -14,5 +14,6 @@ */ module tanya.meta; +public import tanya.meta.metafunction; public import tanya.meta.traits; public import tanya.meta.transform; diff --git a/source/tanya/meta/traits.d b/source/tanya/meta/traits.d index f603ea1..a15b5db 100644 --- a/source/tanya/meta/traits.d +++ b/source/tanya/meta/traits.d @@ -17,6 +17,7 @@ */ module tanya.meta.traits; +import tanya.meta.metafunction; import tanya.meta.transform; /** @@ -966,14 +967,13 @@ pure nothrow @safe @nogc unittest * 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. * - * For all non-classes $(D_INLINECODE isAbstractClass!T) evaluates to - * $(D_KEYWORD false). - * * 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); @@ -1004,20 +1004,18 @@ pure nothrow @safe @nogc unittest static assert(isAbstractClass!C); static assert(isAbstractClass!D); static assert(!isAbstractClass!E); - static assert(!isAbstractClass!int); } /** * Determines whether $(D_PARAM T) is a final class. * - * For all non-classes $(D_INLINECODE isFinalClass!T) evaluates to - * $(D_KEYWORD false). - * * 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); @@ -1038,20 +1036,15 @@ pure nothrow @safe @nogc unittest /** * Determines whether $(D_PARAM T) is an abstract method. * - * For all non-methods $(D_INLINECODE isAbstractFunction!T) evaluates to - * $(D_KEYWORD false). - * * 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). */ -template isAbstractFunction(F...) -if (F.length == 1) -{ - enum bool isAbstractFunction = __traits(isAbstractFunction, F[0]); -} +enum bool isAbstractFunction(alias F) = __traits(isAbstractFunction, F); /// pure nothrow @safe @nogc unittest @@ -1075,26 +1068,20 @@ pure nothrow @safe @nogc unittest static assert(!isAbstractFunction!(A.func)); static assert(isAbstractFunction!(B.func)); static assert(!isAbstractFunction!(C.func)); - static assert(!isAbstractFunction!int); } /** * Determines whether $(D_PARAM T) is a final method. * - * For all non-methods $(D_INLINECODE isFinalFunction!T) evaluates to - * $(D_KEYWORD false). - * * 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). */ -template isFinalFunction(F...) -if (F.length == 1) -{ - enum bool isFinalFunction = __traits(isFinalFunction, F[0]); -} +enum bool isFinalFunction(alias F) = __traits(isFinalFunction, F); /// pure nothrow @safe @nogc unittest @@ -1111,7 +1098,6 @@ pure nothrow @safe @nogc unittest static assert(isFinalFunction!(A.finalFunc)); static assert(!isFinalFunction!(A.virtualFunc)); - static assert(!isFinalFunction!int); } /** @@ -1252,7 +1238,7 @@ template isFunction(F...) if (F.length == 1) { static if (is(F[0] == function) - || is(typeof(&F[0]) U == delegate) + || is(typeof(&F[0]) T == delegate) || (is(typeof(&F[0]) T : T*) && is(T == function))) { enum bool isFunction = true; @@ -1348,7 +1334,8 @@ pure nothrow @safe @nogc unittest template isCallable(F...) if (F.length == 1) { - static if (isSomeFunction!F || (is(typeof(F[0].opCall) U) && isFunction!U)) + static if (isSomeFunction!F + || (is(typeof(F[0].opCall)) && isFunction!(F[0].opCall))) { enum bool isCallable = true; } @@ -1384,6 +1371,20 @@ pure nothrow @safe @nogc unittest static assert(!isCallable!I); } +private pure nothrow @safe @nogc unittest +{ + struct S + { + @property int opCall() + { + return 0; + } + } + S s; + static assert(isCallable!S); + static assert(isCallable!s); +} + /** * Params: * T = Aggregate type. @@ -1403,8 +1404,304 @@ pure nothrow @safe @nogc unittest 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, "member3")); + static assert(hasMember!(S, "member4")); + static assert(!hasMember!(S, "member6")); +} + +/** + * 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; + } +} + +/// +pure nothrow @safe @nogc unittest +{ + 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 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; + } +} + +/// +pure nothrow @safe @nogc unittest +{ + 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))); + } + } +} + +/** + * POD (Plain Old Data) is a $(D_KEYWORD struct) without constructors, + * destructors and member functions. + * + * Params: + * T = A type. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a POD type, + * $(D_KEYWORD false) otherwise. + */ +enum bool isPOD(T) = __traits(isPOD, T); + +/// +pure nothrow @safe @nogc unittest +{ + struct S1 + { + void method() + { + } + } + static assert(!isPOD!S1); + + struct S2 + { + void function() val; // Function pointer, not a member function. + } + static assert(isPOD!S2); + + struct S3 + { + this(this) + { + } + } + static assert(!isPOD!S3); +} + +/** + * 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); +} + +/// +pure nothrow @safe unittest +{ + static struct S + { + } + static assert(!isNested!S); + + class C + { + void method() + { + } + } + static assert(isNested!C); +} + +/** + * Params: + * T = 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); + +/// +pure nothrow @safe @nogc unittest +{ + void func() + { + void nestedFunc() + { + } + static assert(isNestedFunction!nestedFunc); + } +} + +/** + * Params: + * F = A function. + * + * Returns: Type of the function $(D_PARAM F). + */ +template FunctionTypeOf(F...) +if (F.length == 1 && 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)); }