summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2017-08-18 23:38:41 +0200
committerEugen Wissner <belka@caraus.de>2017-08-18 23:38:41 +0200
commit9355c541632a1e39a4ef8daefda49f8cc8055ff9 (patch)
tree01c524d18ce6525390ba1628958a4f5d9c3c7c0e
parente8dd6e321737abd6c0f0542575084e04d5269992 (diff)
downloadtanya-9355c541632a1e39a4ef8daefda49f8cc8055ff9.tar.gz
Add metafunctions
-rw-r--r--source/tanya/meta/metafunction.d208
-rw-r--r--source/tanya/meta/package.d1
-rw-r--r--source/tanya/meta/traits.d353
3 files changed, 534 insertions, 28 deletions
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));
}