summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2017-08-22 11:12:41 +0200
committerEugen Wissner <belka@caraus.de>2017-08-22 11:12:41 +0200
commit666d59c231f2aea29108db07c981664329a314e8 (patch)
treeebdfff6495ac1cae9eb7bb19d2ebbc1eab799072 /source
parentce90b4865b2eb56d3466ca3a4731915c634145b1 (diff)
downloadtanya-666d59c231f2aea29108db07c981664329a314e8.tar.gz
Add traits for checking if class, iface, struct
They are useful for compile-time algorithms like Filter, StaticMap and so on.
Diffstat (limited to 'source')
-rw-r--r--source/tanya/meta/metafunction.d319
-rw-r--r--source/tanya/meta/trait.d441
-rw-r--r--source/tanya/meta/transform.d144
3 files changed, 830 insertions, 74 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)
+{
+ private enum result = cmp!(Args[1], Args[0]);
+ static if (is(typeof(result) == bool))
+ {
+ enum bool isLessEqual = !result;
+ }
+ else
+ {
+ enum bool isLessEqual = result >= 0;
+ }
+}
+
+///
+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)
{
- enum result = cmp!(Args[1], Args[0]);
+ private enum result = cmp!Args;
static if (is(typeof(result) == bool))
{
- enum bool lessEqual = !result;
+ enum bool isGreater = !result && cmp!(Args[1], Args[0]);
}
else
{
- enum bool lessEqual = result >= 0;
+ enum bool isGreater = result > 0;
}
}
-private template equalTo(Args...)
+///
+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)
+ {
+ 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
{
- static assert(i == e);
+ 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;
+
/**
+ * Returns size of the type $(D_PARAM T).
+ *
* Params:
- * F = A function.
+ * T = A type.
+ *
+ * Returns: Size of the type $(D_PARAM T).
+ */
+enum size_t sizeOf(T) = T.sizeof;
+
+///
+pure nothrow @safe @nogc unittest
+{
+ 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: Type of the function $(D_PARAM F).
+ * Returns: Mangled name of $(D_PARAM T).
*/
-template FunctionTypeOf(F...)
-if (isCallable!F)
+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)
{
- 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)))
+ 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));
+ 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));
+
}
-private pure nothrow @safe @nogc unittest
+/**
+ * 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);
+
+///
+pure nothrow @safe @nogc unittest
{
- static assert(is(FunctionTypeOf!(void delegate()) == function));
+ 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)[]));
+}
- static void staticFunc()
+/**
+ * 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 BaseTypeTuple = Tuple;
}
- auto functionPointer = &staticFunc;
- static assert(is(FunctionTypeOf!staticFunc == function));
- static assert(is(FunctionTypeOf!functionPointer == function));
-
- void func()
+ else
{
+ static assert(false, "Argument isn't a class or interface");
}
- auto dg = &func;
- static assert(is(FunctionTypeOf!func == function));
- static assert(is(FunctionTypeOf!dg == function));
+}
- interface I
+///
+pure nothrow @safe @nogc unittest
+{
+ interface I1
+ {
+ }
+ interface I2
+ {
+ }
+ interface I3 : I1, I2
{
- @property int prop();
}
- static assert(is(FunctionTypeOf!(I.prop) == function));
+ interface I4
+ {
+ }
+ class A : I3, I4
+ {
+ }
+ static assert(is(BaseTypeTuple!A == AliasSeq!(Object, I3, I4)));
+ static assert(BaseTypeTuple!Object.length == 0);
+}
- struct S
+/**
+ * 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...)
{
- void opCall()
+ static if (T.length == 0)
{
+ alias Impl = AliasSeq!();
}
- }
- class C
- {
- static void opCall()
+ else
{
+ alias Impl = AliasSeq!(BaseTypeTuple!(T[0]),
+ staticMap!(ImplCopy, BaseTypeTuple!(T[0])));
}
}
- S s;
+ private alias ImplCopy = Impl; // To avoid recursive template expansion.
+ private enum bool cmp(A, B) = is(B == interface) && is(A == class);
- static assert(is(FunctionTypeOf!s == function));
- static assert(is(FunctionTypeOf!C == function));
- static assert(is(FunctionTypeOf!S == function));
+ alias TransitiveBaseTypeTuple = NoDuplicates!(staticSort!(cmp, Impl!T));
}
-private pure nothrow @safe @nogc unittest
+///
+pure nothrow @safe @nogc unittest
{
- struct S2
+ interface I1
+ {
+ }
+ interface I2 : I1
{
- @property int opCall()
- {
- return 0;
- }
}
- S2 s2;
- static assert(is(FunctionTypeOf!S2 == function));
- static assert(is(FunctionTypeOf!s2 == function));
+ 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:
- * F = A callable object.
+ * T = Class type.
*
- * Returns: Return type of $(D_PARAM F).
+ * Returns: Base classes of $(D_PARAM T).
*/
-template ReturnType(F...)
-if (isCallable!F)
+template BaseClassesTuple(T)
+if (is(T == class))
{
- static if (is(FunctionTypeOf!(F[0]) T == return))
+ static if (is(T == Object))
{
- alias ReturnType = T;
+ alias BaseClassesTuple = AliasSeq!();
}
else
{
- static assert(false, "Argument is not a callable");
+ 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));
+}