Complete tanya.meta.metafunction

This commit is contained in:
Eugen Wissner 2017-08-20 12:29:48 +02:00
parent a188f8b6e2
commit beb5d6963b
2 changed files with 598 additions and 20 deletions

View File

@ -15,9 +15,40 @@
*/ */
module tanya.meta.metafunction; module tanya.meta.metafunction;
import tanya.meta.trait;
version (unittest) version (unittest)
{ {
import tanya.meta.trait; import tanya.meta.transform;
}
// Used for sorting.
private template lessEqual(alias cmp, Args...)
if (Args.length == 2)
{
enum result = cmp!(Args[1], Args[0]);
static if (is(typeof(result) == bool))
{
enum bool lessEqual = !result;
}
else
{
enum bool lessEqual = result >= 0;
}
}
private template equalTo(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;
}
else
{
enum bool equalTo = false;
}
} }
/** /**
@ -165,8 +196,7 @@ if (Args.length > 0)
{ {
enum ptrdiff_t indexOf = -1; enum ptrdiff_t indexOf = -1;
} }
else static if (is(Args[0] == Args[1]) else static if (equalTo!(Args[0 .. 2]))
|| (is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1])))
{ {
enum ptrdiff_t indexOf = i; enum ptrdiff_t indexOf = i;
} }
@ -185,7 +215,7 @@ if (Args.length > 0)
* T = The type to search for. * T = The type to search for.
* L = Type list. * L = Type list.
* *
* Returns: The index of the first occurence of $(D_PARAM T) in $(D_PARAM L). * Returns: The index of the first occurrence of $(D_PARAM T) in $(D_PARAM L).
*/ */
template staticIndexOf(T, L...) template staticIndexOf(T, L...)
{ {
@ -346,8 +376,8 @@ pure nothrow @safe @nogc unittest
* ) * )
* *
* Params: * Params:
* cmp = Comparison template. * cmp = Sorting template predicate.
* L = Arguments. * L = Elements to be tested.
* *
* Returns: $(D_KEYWORD true) if $(D_PARAM L) is sorted, $(D_KEYWORD false) * Returns: $(D_KEYWORD true) if $(D_PARAM L) is sorted, $(D_KEYWORD false)
* if not. * if not.
@ -360,19 +390,10 @@ template staticIsSorted(alias cmp, L...)
} }
else else
{ {
// `L` is sorted if the both halves are sorted. // `L` is sorted if the both halves and the boundary values are sorted.
enum bool halves = staticIsSorted!(cmp, L[0 .. $ / 2]) enum bool staticIsSorted = lessEqual!(cmp, L[$ / 2 - 1], L[$ / 2])
&& staticIsSorted!(cmp, L[0 .. $ / 2])
&& staticIsSorted!(cmp, L[$ / 2 .. $]); && staticIsSorted!(cmp, L[$ / 2 .. $]);
// Compare the boundary values of the havles.
enum result = cmp!(L[$ / 2], L[$ / 2 - 1]);
static if (is(typeof(result) == bool))
{
enum bool staticIsSorted = !result && halves;
}
else
{
enum bool staticIsSorted = result >= 0 && halves;
}
} }
} }
@ -451,3 +472,541 @@ pure nothrow @safe @nogc unittest
static assert(intIs!(isIntegral)); static assert(intIs!(isIntegral));
static assert(!intIs!(isUnsigned)); static assert(!intIs!(isUnsigned));
} }
/**
* Params:
* n = The number of times to repeat $(D_PARAM L).
* L = The sequence to be repeated.
*
* Returns: $(D_PARAM L) repeated $(D_PARAM n) times.
*/
template Repeat(size_t n, L...)
if (n > 0)
{
static if (n == 1)
{
alias Repeat = L;
}
else
{
alias Repeat = AliasSeq!(L, Repeat!(n - 1, L));
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(Repeat!(1, uint, int) == AliasSeq!(uint, int)));
static assert(is(Repeat!(2, uint, int) == AliasSeq!(uint, int, uint, int)));
static assert(is(Repeat!(3) == AliasSeq!()));
}
private template ReplaceOne(L...)
{
static if (L.length == 2)
{
alias ReplaceOne = AliasSeq!();
}
else static if (equalTo!(L[0], L[2]))
{
alias ReplaceOne = AliasSeq!(L[1], L[3 .. $]);
}
else
{
alias ReplaceOne = AliasSeq!(L[2], ReplaceOne!(L[0], L[1], L[3 .. $]));
}
}
/**
* Replaces the first occurrence of $(D_PARAM T) in $(D_PARAM L) with
* $(D_PARAM U).
*
* Params:
* T = The symbol to be replaced.
* U = Replacement.
* L = List of symbols.
*
* Returns: $(D_PARAM L) with the first occurrence of $(D_PARAM T) replaced.
*/
template Replace(T, U, L...)
{
alias Replace = ReplaceOne!(T, U, L);
}
/// Ditto.
template Replace(alias T, U, L...)
{
alias Replace = ReplaceOne!(T, U, L);
}
/// Ditto.
template Replace(T, alias U, L...)
{
alias Replace = ReplaceOne!(T, U, L);
}
/// Ditto.
template Replace(alias T, alias U, L...)
{
alias Replace = ReplaceOne!(T, U, L);
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(Replace!(int, uint, int) == AliasSeq!(uint)));
static assert(is(Replace!(int, uint, short, int, int, ushort)
== AliasSeq!(short, uint, int, ushort)));
static assert(Replace!(5, 8, 1, 2, 5, 5) == AliasSeq!(1, 2, 8, 5));
}
private template ReplaceAllImpl(L...)
{
static if (L.length == 2)
{
alias ReplaceAllImpl = AliasSeq!();
}
else
{
private alias Rest = ReplaceAllImpl!(L[0], L[1], L[3 .. $]);
static if (equalTo!(L[0], L[2]))
{
alias ReplaceAllImpl = AliasSeq!(L[1], Rest);
}
else
{
alias ReplaceAllImpl = AliasSeq!(L[2], Rest);
}
}
}
/**
* Replaces all occurrences of $(D_PARAM T) in $(D_PARAM L) with $(D_PARAM U).
*
* Params:
* T = The symbol to be replaced.
* U = Replacement.
* L = List of symbols.
*
* Returns: $(D_PARAM L) with all occurrences of $(D_PARAM T) replaced.
*/
template ReplaceAll(T, U, L...)
{
alias ReplaceAll = ReplaceAllImpl!(T, U, L);
}
/// Ditto.
template ReplaceAll(alias T, U, L...)
{
alias ReplaceAll = ReplaceAllImpl!(T, U, L);
}
/// Ditto.
template ReplaceAll(T, alias U, L...)
{
alias ReplaceAll = ReplaceAllImpl!(T, U, L);
}
/// Ditto.
template ReplaceAll(alias T, alias U, L...)
{
alias ReplaceAll = ReplaceAllImpl!(T, U, L);
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(ReplaceAll!(int, uint, int) == AliasSeq!(uint)));
static assert(is(ReplaceAll!(int, uint, short, int, int, ushort)
== AliasSeq!(short, uint, uint, ushort)));
static assert(ReplaceAll!(5, 8, 1, 2, 5, 5) == AliasSeq!(1, 2, 8, 8));
}
/**
* Params:
* L = List of symbols.
*
* Returns: $(D_PARAM L) with elements in reversed order.
*/
template Reverse(L...)
{
static if (L.length == 0)
{
alias Reverse = AliasSeq!();
}
else
{
alias Reverse = AliasSeq!(L[$ - 1], Reverse!(L[0 .. $ - 1]));
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(Reverse!(byte, short, int) == AliasSeq!(int, short, byte)));
}
/**
* Applies $(D_PARAM F) to all elements of $(D_PARAM T).
*
* Params:
* F = Template predicate.
* T = List of symbols.
*
* Returns: Elements $(D_PARAM T) after applying $(D_PARAM F) to them.
*/
template staticMap(alias F, T...)
{
static if (T.length == 0)
{
alias staticMap = AliasSeq!();
}
else
{
alias staticMap = AliasSeq!(F!(T[0]), staticMap!(F, T[1 .. $]));
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(staticMap!(Unqual, const int, immutable short)
== AliasSeq!(int, short)));
}
/**
* Sorts $(D_PARAM L) in ascending order according to $(D_PARAM cmp).
*
* $(D_PARAM cmp) can evaluate to:
* $(UL
* $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
* $(D_INLINECODE a[i] < a[i + 1]).)
* $(LI $(D_KEYWORD int): a negative number means that
* $(D_INLINECODE a[i] < a[i + 1]), a positive number that
* $(D_INLINECODE a[i] > a[i + 1]), `0` if they equal.)
* )
*
* Merge sort is used to sort the arguments.
*
* Params:
* cmp = Sorting template predicate.
* L = Elements to be sorted.
*
* Returns: Elements of $(D_PARAM L) in ascending order.
*
* See_Also: $(LINK2 https://en.wikipedia.org/wiki/Merge_sort, Merge sort).
*/
template staticSort(alias cmp, L...)
{
private template merge(size_t A, size_t B)
{
static if (A + B == L.length)
{
alias merge = AliasSeq!();
}
else static if (B >= Right.length
|| (A < Left.length && lessEqual!(cmp, Left[A], Right[B])))
{
alias merge = AliasSeq!(Left[A], merge!(A + 1, B));
}
else
{
alias merge = AliasSeq!(Right[B], merge!(A, B + 1));
}
}
static if (L.length <= 1)
{
alias staticSort = L;
}
else
{
private alias Left = staticSort!(cmp, L[0 .. $ / 2]);
private alias Right = staticSort!(cmp, L[$ / 2 .. $]);
alias staticSort = merge!(0, 0);
}
}
///
pure nothrow @safe @nogc unittest
{
enum cmp(T, U) = T.sizeof < U.sizeof;
static assert(is(staticSort!(cmp, long, short, byte, int)
== AliasSeq!(byte, short, int, long)));
}
private pure nothrow @safe @nogc unittest
{
enum cmp(int T, int U) = T - U;
static assert(staticSort!(cmp, 5, 17, 9, 12, 2, 10, 14)
== AliasSeq!(2, 5, 9, 10, 12, 14, 17));
}
private enum bool DerivedToFrontCmp(A, B) = is(A : B);
/**
* Returns $(D_PARAM L) sorted in such a way that the most derived types come
* first.
*
* Params:
* L = Type tuple.
*
* Returns: Sorted $(D_PARAM L).
*/
template DerivedToFront(L...)
{
alias DerivedToFront = staticSort!(DerivedToFrontCmp, L);
}
///
pure nothrow @safe @nogc unittest
{
class A
{
}
class B : A
{
}
class C : B
{
}
static assert(is(DerivedToFront!(B, A, C) == AliasSeq!(C, B, A)));
}
/**
* Returns the type from the type tuple $(D_PARAM L) that is most derived from
* $(D_PARAM T).
*
* Params:
* T = The type to compare to.
* L = Type tuple.
*
* Returns: The type most derived from $(D_PARAM T).
*/
template MostDerived(T, L...)
{
static if (L.length == 0)
{
alias MostDerived = T;
}
else static if (is(T : L[0]))
{
alias MostDerived = MostDerived!(T, L[1 .. $]);
}
else
{
alias MostDerived = MostDerived!(L[0], L[1 .. $]);
}
}
///
pure nothrow @safe @nogc unittest
{
class A
{
}
class B : A
{
}
class C : B
{
}
static assert(is(MostDerived!(A, C, B) == C));
}
private template EraseOne(L...)
if (L.length > 0)
{
static if (L.length == 1)
{
alias EraseOne = AliasSeq!();
}
else static if (equalTo!(L[0 .. 2]))
{
alias EraseOne = AliasSeq!(L[2 .. $]);
}
else
{
alias EraseOne = AliasSeq!(L[1], EraseOne!(L[0], L[2 .. $]));
}
}
/**
* Removes the first occurrence of $(D_PARAM T) from the alias sequence
* $(D_PARAL L).
*
* Params:
* T = The item to be removed.
* L = Alias sequence.
*
* Returns: $(D_PARAM L) with the first occurrence of $(D_PARAM T) removed.
*/
template Erase(T, L...)
{
alias Erase = EraseOne!(T, L);
}
/// Ditto.
template Erase(alias T, L...)
{
alias Erase = EraseOne!(T, L);
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(Erase!(int, short, int, int, uint) == AliasSeq!(short, int, uint)));
static assert(is(Erase!(int, short, uint) == AliasSeq!(short, uint)));
}
private template EraseAllImpl(L...)
{
static if (L.length == 1)
{
alias EraseAllImpl = AliasSeq!();
}
else static if (equalTo!(L[0 .. 2]))
{
alias EraseAllImpl = EraseAllImpl!(L[0], L[2 .. $]);
}
else
{
alias EraseAllImpl = AliasSeq!(L[1], EraseAllImpl!(L[0], L[2 .. $]));
}
}
/**
* Removes all occurrences of $(D_PARAM T) from the alias sequence $(D_PARAL L).
*
* Params:
* T = The item to be removed.
* L = Alias sequence.
*
* Returns: $(D_PARAM L) with all occurrences of $(D_PARAM T) removed.
*/
template EraseAll(T, L...)
{
alias EraseAll = EraseAllImpl!(T, L);
}
/// Ditto.
template EraseAll(alias T, L...)
{
alias EraseAll = EraseAllImpl!(T, L);
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(EraseAll!(int, short, int, int, uint) == AliasSeq!(short, uint)));
static assert(is(EraseAll!(int, short, uint) == AliasSeq!(short, uint)));
static assert(is(EraseAll!(int, int, int) == AliasSeq!()));
}
/**
* Returns an alias sequence which contains only items that satisfy the
* condition $(D_PARAM pred).
*
* Params:
* pred = Template predicate.
* L = Alias sequence.
*
* Returns: $(D_PARAM L) filtered so that it contains only items that satisfy
* $(D_PARAM pred).
*/
template Filter(alias pred, L...)
{
static if (L.length == 0)
{
alias Filter = AliasSeq!();
}
else static if (pred!(L[0]))
{
alias Filter = AliasSeq!(L[0], Filter!(pred, L[1 .. $]));
}
else
{
alias Filter = Filter!(pred, L[1 .. $]);
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(Filter!(isIntegral, real, int, bool, uint) == AliasSeq!(int, uint)));
}
/**
* Removes all duplicates from the alias sequence $(D_PARAM L).
*
* Params:
* L = Alias sequence.
*
* Returns: $(D_PARAM L) containing only unique items.
*/
template NoDuplicates(L...)
{
static if (L.length == 0)
{
alias NoDuplicates = AliasSeq!();
}
else
{
private alias Rest = NoDuplicates!(EraseAll!(L[0], L[1 .. $]));
alias NoDuplicates = AliasSeq!(L[0], Rest);
}
}
///
pure nothrow @safe @nogc unittest
{
alias Types = AliasSeq!(int, uint, int, short, short, uint);
static assert(is(NoDuplicates!Types == AliasSeq!(int, uint, short)));
}
/**
* Converts an input range $(D_PARAM range) into an alias sequence.
*
* Params:
* range = Input range.
*
* Returns: Alias sequence with items from $(D_PARAM range).
*/
template aliasSeqOf(alias range)
{
static if (isArray!(typeof(range)))
{
static if (range.length == 0)
{
alias aliasSeqOf = AliasSeq!();
}
else
{
alias aliasSeqOf = AliasSeq!(range[0], aliasSeqOf!(range[1 .. $]));
}
}
else
{
ReturnType!(typeof(&range.front))[] toArray(typeof(range) range)
{
typeof(return) result;
foreach (r; range)
{
result ~= r;
}
return result;
}
alias aliasSeqOf = aliasSeqOf!(toArray(range));
}
}
///
pure nothrow @safe @nogc unittest
{
foreach (i, e; aliasSeqOf!([0, 1, 2, 3]))
{
static assert(i == e);
}
}

View File

@ -1624,7 +1624,7 @@ pure nothrow @safe @nogc unittest
* Returns: Type of the function $(D_PARAM F). * Returns: Type of the function $(D_PARAM F).
*/ */
template FunctionTypeOf(F...) template FunctionTypeOf(F...)
if (F.length == 1 && isCallable!F) if (isCallable!F)
{ {
static if ((is(typeof(F[0]) T : T*) && is(T == function)) static if ((is(typeof(F[0]) T : T*) && is(T == function))
|| (is(F[0] T : T*) && is(T == function)) || (is(F[0] T : T*) && is(T == function))
@ -1705,3 +1705,22 @@ private pure nothrow @safe @nogc unittest
static assert(is(FunctionTypeOf!S2 == function)); static assert(is(FunctionTypeOf!S2 == function));
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");
}
}