Add traits for checking if class, iface, struct

They are useful for compile-time algorithms like Filter, StaticMap and
so on.
This commit is contained in:
Eugen Wissner 2017-08-22 11:12:41 +02:00
parent ce90b4865b
commit 666d59c231
3 changed files with 848 additions and 92 deletions

View File

@ -15,42 +15,264 @@
*/ */
module tanya.meta.metafunction; module tanya.meta.metafunction;
import tanya.meta.trait; import tanya.meta.transform;
version (unittest) 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) 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)) static if (is(typeof(result) == bool))
{ {
enum bool lessEqual = !result; enum bool isLessEqual = !result;
} }
else 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) if (Args.length == 2)
{ {
static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1])) static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1]))
|| is(Args[0] == Args[1])) || is(Args[0] == Args[1]))
{ {
enum bool equalTo = true; enum bool isEqual = true;
} }
else 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). * Creates an alias for $(D_PARAM T).
* *
@ -196,7 +418,7 @@ if (Args.length > 0)
{ {
enum ptrdiff_t indexOf = -1; enum ptrdiff_t indexOf = -1;
} }
else static if (equalTo!(Args[0 .. 2])) else static if (isEqual!(Args[0 .. 2]))
{ {
enum ptrdiff_t indexOf = i; enum ptrdiff_t indexOf = i;
} }
@ -391,7 +613,7 @@ template staticIsSorted(alias cmp, L...)
else else
{ {
// `L` is sorted if the both halves and the boundary values are sorted. // `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[0 .. $ / 2])
&& staticIsSorted!(cmp, L[$ / 2 .. $]); && staticIsSorted!(cmp, L[$ / 2 .. $]);
} }
@ -507,7 +729,7 @@ private template ReplaceOne(L...)
{ {
alias ReplaceOne = AliasSeq!(); 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 .. $]); alias ReplaceOne = AliasSeq!(L[1], L[3 .. $]);
} }
@ -570,7 +792,7 @@ private template ReplaceAllImpl(L...)
else else
{ {
private alias Rest = ReplaceAllImpl!(L[0], L[1], L[3 .. $]); 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); alias ReplaceAllImpl = AliasSeq!(L[1], Rest);
} }
@ -707,7 +929,7 @@ template staticSort(alias cmp, L...)
alias merge = AliasSeq!(); alias merge = AliasSeq!();
} }
else static if (B >= Right.length 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)); alias merge = AliasSeq!(Left[A], merge!(A + 1, B));
} }
@ -823,7 +1045,7 @@ if (L.length > 0)
{ {
alias EraseOne = AliasSeq!(); alias EraseOne = AliasSeq!();
} }
else static if (equalTo!(L[0 .. 2])) else static if (isEqual!(L[0 .. 2]))
{ {
alias EraseOne = AliasSeq!(L[2 .. $]); alias EraseOne = AliasSeq!(L[2 .. $]);
} }
@ -867,7 +1089,7 @@ private template EraseAllImpl(L...)
{ {
alias EraseAllImpl = AliasSeq!(); alias EraseAllImpl = AliasSeq!();
} }
else static if (equalTo!(L[0 .. 2])) else static if (isEqual!(L[0 .. 2]))
{ {
alias EraseAllImpl = EraseAllImpl!(L[0], L[2 .. $]); alias EraseAllImpl = EraseAllImpl!(L[0], L[2 .. $]);
} }
@ -1005,8 +1227,67 @@ template aliasSeqOf(alias range)
/// ///
pure nothrow @safe @nogc unittest 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 assert(i == e); 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
{
alias Select = L[1];
} }
} }

View File

@ -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: * Returns size of the type $(D_PARAM T).
* F = A function.
* *
* Returns: Type of the function $(D_PARAM F). * Params:
* T = A type.
*
* Returns: Size of the type $(D_PARAM T).
*/ */
template FunctionTypeOf(F...) enum size_t sizeOf(T) = T.sizeof;
if (isCallable!F)
///
pure nothrow @safe @nogc unittest
{ {
static if ((is(typeof(F[0]) T : T*) && is(T == function)) static assert(sizeOf!(bool function()) == size_t.sizeof);
|| (is(F[0] T : T*) && is(T == function)) static assert(sizeOf!bool == 1);
|| is(F[0] T == delegate) static assert(sizeOf!short == 2);
|| is(typeof(F[0]) T == delegate) static assert(sizeOf!int == 4);
|| is(F[0] T == function) static assert(sizeOf!long == 8);
|| is(typeof(&F[0]) T == delegate) static assert(sizeOf!(void[16]) == 16);
|| (is(typeof(&F[0]) T : T*) && is(T == function))) }
/**
* 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)
{ {
alias FunctionTypeOf = T; 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)
{
}
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 else
{ {
alias FunctionTypeOf = FunctionTypeOf!(F[0].opCall); enum bool isInstanceOf = false;
} }
} }
/// ///
pure nothrow @safe @nogc unittest pure nothrow @safe @nogc unittest
{ {
static assert(is(FunctionTypeOf!(void function()) == function)); struct S(T)
static assert(is(FunctionTypeOf!(() {}) == function)); {
} }
static assert(isInstanceOf!(S, S!int));
private pure nothrow @safe @nogc unittest static void func(T)()
{ {
static assert(is(FunctionTypeOf!(void delegate()) == function)); }
static assert(isInstanceOf!(func, func!int));
static void staticFunc() template T(U)
{ {
} }
auto functionPointer = &staticFunc; static assert(isInstanceOf!(T, T!int));
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: * Checks whether $(D_PARAM From) is implicitly (without explicit
* F = A callable object. * $(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...) enum bool isImplicitlyConvertible(From, To) = is(From : To);
if (isCallable!F)
///
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)))
{ {
alias ReturnType = T; static if (is(T Tuple == super))
{
alias BaseTypeTuple = Tuple;
} }
else 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));
}

View File

@ -473,3 +473,147 @@ pure nothrow @safe @nogc unittest
static assert(is(Promoted!(const bool) == const int)); static assert(is(Promoted!(const bool) == const int));
static assert(is(Promoted!(shared bool) == shared 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));
}