918 lines
25 KiB
D
918 lines
25 KiB
D
/* 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/. */
|
|
|
|
/**
|
|
* Template metaprogramming.
|
|
*
|
|
* This package contains utilities to acquire type information at compile-time,
|
|
* to transform from one type to another. It has also different algorithms for
|
|
* iterating, searching and modifying template arguments.
|
|
*
|
|
* Copyright: Eugen Wissner 2017-2026.
|
|
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
|
* Mozilla Public License, v. 2.0).
|
|
*/
|
|
module tanya.meta;
|
|
|
|
import std.meta;
|
|
import std.traits;
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a wide string, i.e. consists of
|
|
* $(D_KEYWORD dchar).
|
|
*
|
|
* The character type of the string and the string itself can have any type
|
|
* qualifiers.
|
|
*
|
|
* Static $(D_KEYWORD dchar) arrays are not considered strings.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a wide string,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isWideString(T) = is(immutable T == immutable dchar[]);
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
static assert(isWideString!(dchar[]));
|
|
static assert(!isWideString!(char[]));
|
|
static assert(!isWideString!(wchar[]));
|
|
|
|
static assert(isWideString!dstring);
|
|
static assert(!isWideString!string);
|
|
static assert(!isWideString!wstring);
|
|
|
|
static assert(isWideString!(const dstring));
|
|
static assert(!isWideString!(const string));
|
|
static assert(!isWideString!(const wstring));
|
|
|
|
static assert(isWideString!(shared dstring));
|
|
static assert(!isWideString!(shared string));
|
|
static assert(!isWideString!(shared wstring));
|
|
|
|
static assert(isWideString!(const(dchar)[]));
|
|
static assert(isWideString!(inout(dchar)[]));
|
|
static assert(isWideString!(shared(const(dchar))[]));
|
|
static assert(isWideString!(shared(dchar)[]));
|
|
static assert(!isWideString!(dchar[10]));
|
|
}
|
|
|
|
/*
|
|
* Tests 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.
|
|
*/
|
|
private enum bool isInterface(T) = is(T == interface);
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a polymorphic type, i.e. a
|
|
* $(D_KEYWORD class) or an $(D_KEYWORD interface).
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a $(D_KEYWORD class) or an
|
|
* $(D_KEYWORD interface), $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isPolymorphicType(T) = is(T == class) || is(T == interface);
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
interface I
|
|
{
|
|
}
|
|
static assert(isPolymorphicType!Object);
|
|
static assert(isPolymorphicType!I);
|
|
static assert(!isPolymorphicType!short);
|
|
}
|
|
|
|
/**
|
|
* Returns the size in bytes of the state that needs to be allocated to hold an
|
|
* object of type $(D_PARAM T).
|
|
*
|
|
* There is a difference between the `.sizeof`-property and
|
|
* $(D_PSYMBOL stateSize) if $(D_PARAM T) is a class or an interface.
|
|
* `T.sizeof` is constant on the given architecture then and is the same as
|
|
* `size_t.sizeof` and `ptrdiff_t.sizeof`. This is because classes and
|
|
* interfaces are reference types and `.sizeof` returns the size of the
|
|
* reference which is the same as the size of a pointer. $(D_PSYMBOL stateSize)
|
|
* returns the size of the instance itself.
|
|
*
|
|
* The size of a dynamic array is `size_t.sizeof * 2` since a dynamic array
|
|
* stores its length and a data pointer. The size of the static arrays is
|
|
* calculated differently since they are value types. It is the array length
|
|
* multiplied by the element size.
|
|
*
|
|
* `stateSize!void` is `1` since $(D_KEYWORD void) is mostly used as a synonym
|
|
* for $(D_KEYWORD byte)/$(D_KEYWORD ubyte) in `void*`.
|
|
*
|
|
* Params:
|
|
* T = Object type.
|
|
*
|
|
* Returns: Size of an instance of type $(D_PARAM T).
|
|
*/
|
|
template stateSize(T)
|
|
{
|
|
static if (isPolymorphicType!T)
|
|
{
|
|
enum size_t stateSize = __traits(classInstanceSize, T);
|
|
}
|
|
else
|
|
{
|
|
enum size_t stateSize = T.sizeof;
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
static assert(stateSize!int == 4);
|
|
static assert(stateSize!bool == 1);
|
|
static assert(stateSize!(int[]) == (size_t.sizeof * 2));
|
|
static assert(stateSize!(short[3]) == 6);
|
|
|
|
static struct Empty
|
|
{
|
|
}
|
|
static assert(stateSize!Empty == 1);
|
|
static assert(stateSize!void == 1);
|
|
}
|
|
|
|
/**
|
|
* Finds the minimum value in $(D_PARAM Args) according to $(D_PARAM pred).
|
|
*
|
|
* $(D_PARAM Args) should contain at least one element.
|
|
*
|
|
* $(D_PARAM pred) 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:
|
|
* pred = Template predicate.
|
|
* Args = Elements for which you want to find the minimum value.
|
|
*
|
|
* Returns: The minimum.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isLess).
|
|
*/
|
|
template Min(alias pred, Args...)
|
|
if (Args.length > 0 && __traits(isTemplate, pred))
|
|
{
|
|
static if (Args.length == 1)
|
|
{
|
|
alias Min = Alias!(Args[0]);
|
|
}
|
|
else static if (isLess!(pred, Args[1], Args[0]))
|
|
{
|
|
alias Min = Min!(pred, Args[1], Args[2 .. $]);
|
|
}
|
|
else
|
|
{
|
|
alias Min = Min!(pred, Args[0], Args[2 .. $]);
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
enum bool cmp(alias T, alias U) = T < U;
|
|
static assert(Min!(cmp, 8, 4, 5, 3, 13) == 3);
|
|
static assert(Min!(cmp, 8) == 8);
|
|
}
|
|
|
|
/**
|
|
* Finds the maximum value in $(D_PARAM Args) according to $(D_PARAM pred).
|
|
*
|
|
* $(D_PARAM Args) should contain at least one element.
|
|
*
|
|
* $(D_PARAM pred) 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:
|
|
* pred = Template predicate.
|
|
* Args = Elements for which you want to find the maximum value.
|
|
*
|
|
* Returns: The maximum.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isLess).
|
|
*/
|
|
template Max(alias pred, Args...)
|
|
if (Args.length > 0 && __traits(isTemplate, pred))
|
|
{
|
|
static if (Args.length == 1)
|
|
{
|
|
alias Max = Alias!(Args[0]);
|
|
}
|
|
else static if (isGreater!(pred, Args[1], Args[0]))
|
|
{
|
|
alias Max = Max!(pred, Args[1], Args[2 .. $]);
|
|
}
|
|
else
|
|
{
|
|
alias Max = Max!(pred, Args[0], Args[2 .. $]);
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
enum bool cmp(alias T, alias U) = T < U;
|
|
static assert(Max!(cmp, 8, 4, 5, 3, 13) == 13);
|
|
static assert(Max!(cmp, 8) == 8);
|
|
}
|
|
|
|
/**
|
|
* Zips one or more $(D_PSYMBOL Pack)s with $(D_PARAM f).
|
|
*
|
|
* Given $(D_PARAM f) and tuples t1, t2, ..., tk, where tk[i] denotes the
|
|
* $(I i)-th element of the tuple $(I k)-th tuple, $(D_PSYMBOL ZipWith)
|
|
* produces a sequence:
|
|
*
|
|
* ---
|
|
* f(t1[0], t2[0], ... tk[0]),
|
|
* f(t1[1], t2[1], ... tk[1]),
|
|
* ...
|
|
* f(tk[0], tk[1], ... tk[i]),
|
|
* ---
|
|
*
|
|
* $(D_PSYMBOL ZipWith) begins with the first elements from $(D_PARAM Packs)
|
|
* and applies $(D_PARAM f) to them, then it takes the second
|
|
* ones and does the same, and so on.
|
|
*
|
|
* If not all argument tuples have the same length, $(D_PSYMBOL ZipWith) will
|
|
* zip only `n` elements from each tuple, where `n` is the length of the
|
|
* shortest tuple in the argument list. Remaining elements in the longer tuples
|
|
* are just ignored.
|
|
*
|
|
* Params:
|
|
* f = Some template that can be applied to the elements of
|
|
* $(D_PARAM Packs).
|
|
* Packs = $(D_PSYMBOL Pack) instances.
|
|
*
|
|
* Returns: A sequence, whose $(I i)-th element contains the $(I i)-th element
|
|
* from each of the $(D_PARAM Packs).
|
|
*/
|
|
template ZipWith(alias f, Packs...)
|
|
if (Packs.length > 0
|
|
&& __traits(isTemplate, f)
|
|
&& (allSatisfy!(ApplyLeft!(isInstanceOf, Pack), Packs)
|
|
|| allSatisfy!(ApplyLeft!(isInstanceOf, Tuple), Packs)))
|
|
{
|
|
private template GetIth(size_t i, Args...)
|
|
{
|
|
static if ((Args.length == 0) || (Args[0].Seq.length <= i))
|
|
{
|
|
alias GetIth = AliasSeq!();
|
|
}
|
|
else
|
|
{
|
|
alias GetIth = AliasSeq!(Args[0].Seq[i], GetIth!(i, Args[1 .. $]));
|
|
}
|
|
}
|
|
private template Iterate(size_t i, Args...)
|
|
{
|
|
alias Pack = GetIth!(i, Args);
|
|
|
|
static if (Pack.length < Packs.length)
|
|
{
|
|
alias Iterate = AliasSeq!();
|
|
}
|
|
else
|
|
{
|
|
alias Iterate = AliasSeq!(f!Pack, Iterate!(i + 1, Args));
|
|
}
|
|
}
|
|
alias ZipWith = Iterate!(0, Packs);
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
alias Result1 = ZipWith!(AliasSeq, Pack!(1, 2), Pack!(5, 6), Pack!(9, 10));
|
|
static assert(Result1 == AliasSeq!(1, 5, 9, 2, 6, 10));
|
|
|
|
alias Result2 = ZipWith!(AliasSeq, Pack!(1, 2, 3), Pack!(4, 5));
|
|
static assert(Result2 == AliasSeq!(1, 4, 2, 5));
|
|
|
|
alias Result3 = ZipWith!(AliasSeq, Pack!(), Pack!(4, 5));
|
|
static assert(Result3.length == 0);
|
|
}
|
|
|
|
/**
|
|
* Holds a typed sequence of template parameters.
|
|
*
|
|
* Different than $(D_PSYMBOL AliasSeq), $(D_PSYMBOL Pack) doesn't unpack
|
|
* its template parameters automatically. Consider:
|
|
*
|
|
* ---
|
|
* template A(Args...)
|
|
* {
|
|
* static assert(Args.length == 4);
|
|
* }
|
|
*
|
|
* alias AInstance = A!(AliasSeq!(int, uint), AliasSeq!(float, double));
|
|
* ---
|
|
*
|
|
* Using $(D_PSYMBOL AliasSeq) template `A` gets 4 parameters instead of 2,
|
|
* because $(D_PSYMBOL AliasSeq) is just an alias for its template parameters.
|
|
*
|
|
* With $(D_PSYMBOL Pack) it is possible to pass distinguishable
|
|
* sequences of parameters to a template. So:
|
|
*
|
|
* ---
|
|
* template B(Args...)
|
|
* {
|
|
* static assert(Args.length == 2);
|
|
* }
|
|
*
|
|
* alias BInstance = B!(Pack!(int, uint), Pack!(float, double));
|
|
* ---
|
|
*
|
|
* Params:
|
|
* Args = Elements of this $(D_PSYMBOL Pack).
|
|
*
|
|
* See_Also: $(D_PSYMBOL AliasSeq).
|
|
*/
|
|
struct Pack(Args...)
|
|
{
|
|
/// Elements in this tuple as $(D_PSYMBOL AliasSeq).
|
|
alias Seq = Args;
|
|
|
|
/// The length of the tuple.
|
|
enum size_t length = Args.length;
|
|
|
|
alias Seq this;
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
alias A = Pack!short;
|
|
alias B = Pack!(3, 8, 9);
|
|
alias C = Pack!(A, B);
|
|
|
|
static assert(C.length == 2);
|
|
|
|
static assert(A.length == 1);
|
|
static assert(is(A.Seq == AliasSeq!short));
|
|
static assert(B.length == 3);
|
|
static assert(B.Seq == AliasSeq!(3, 8, 9));
|
|
|
|
alias D = Pack!();
|
|
static assert(D.length == 0);
|
|
static assert(is(D.Seq == AliasSeq!()));
|
|
}
|
|
|
|
/**
|
|
* Unordered sequence of unique aliases.
|
|
*
|
|
* $(D_PARAM Args) can contain duplicates, but they will be filtered out, so
|
|
* $(D_PSYMBOL Set) contains only unique items. $(D_PSYMBOL isEqual) is used
|
|
* for determining if two items are equal.
|
|
*
|
|
* Params:
|
|
* Args = Elements of this $(D_PSYMBOL Set).
|
|
*/
|
|
struct Set(Args...)
|
|
{
|
|
/// Elements in this set as $(D_PSYMBOL AliasSeq).
|
|
alias Seq = NoDuplicates!Args;
|
|
|
|
/// The length of the set.
|
|
enum size_t length = Seq.length;
|
|
|
|
alias Seq this;
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
alias S1 = Set!(int, 5, 5, int, 4);
|
|
static assert(S1.length == 3);
|
|
}
|
|
|
|
/**
|
|
* Produces a $(D_PSYMBOL Set) containing all elements of the given
|
|
* $(D_PARAM Sets).
|
|
*
|
|
* Params:
|
|
* Sets = List of $(D_PSYMBOL Set) instances.
|
|
*
|
|
* Returns: Set-theoretic union of all $(D_PARAM Sets).
|
|
*
|
|
* See_Also: $(D_PSYMBOL Set).
|
|
*/
|
|
template Union(Sets...)
|
|
if (allSatisfy!(ApplyLeft!(isInstanceOf, Set), Sets))
|
|
{
|
|
private template Impl(Sets...)
|
|
{
|
|
static if (Sets.length == 0)
|
|
{
|
|
alias Impl = AliasSeq!();
|
|
}
|
|
else
|
|
{
|
|
alias Impl = AliasSeq!(Sets[0].Seq, Impl!(Sets[1 .. $]));
|
|
}
|
|
}
|
|
alias Union = Set!(Impl!Sets);
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
alias S1 = Set!(2, 5, 8, 4);
|
|
alias S2 = Set!(3, 8, 4, 1);
|
|
static assert(Union!(S1, S2).Seq == AliasSeq!(2, 5, 8, 4, 3, 1));
|
|
}
|
|
|
|
/**
|
|
* Produces a $(D_PSYMBOL Set) that containing elements of
|
|
* $(D_INLINECODE Sets[0]) that are also elements of all other sets in
|
|
* $(D_PARAM Sets).
|
|
*
|
|
* Params:
|
|
* Sets = List of $(D_PSYMBOL Set) instances.
|
|
*
|
|
* Returns: Set-theoretic intersection of all $(D_PARAM Sets).
|
|
*
|
|
* See_Also: $(D_PSYMBOL Set).
|
|
*/
|
|
template Intersection(Sets...)
|
|
if (allSatisfy!(ApplyLeft!(isInstanceOf, Set), Sets))
|
|
{
|
|
private template Impl(Args...)
|
|
if (Args.length > 0)
|
|
{
|
|
alias Equal = ApplyLeft!(isEqual, Args[0]);
|
|
static if (Args.length == 1)
|
|
{
|
|
enum bool Impl = true;
|
|
}
|
|
else static if (!anySatisfy!(Equal, Args[1].Seq))
|
|
{
|
|
enum bool Impl = false;
|
|
}
|
|
else
|
|
{
|
|
enum bool Impl = Impl!(Args[0], Args[2 .. $]);
|
|
}
|
|
}
|
|
|
|
private enum bool FilterImpl(Args...) = Impl!(Args[0], Sets[1 .. $]);
|
|
|
|
static if (Sets.length == 0)
|
|
{
|
|
alias Intersection = Set!();
|
|
}
|
|
else
|
|
{
|
|
alias Intersection = Set!(Filter!(FilterImpl, Sets[0].Seq));
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
alias S1 = Set!(2, 5, 8, 4);
|
|
alias S2 = Set!(3, 8, 4, 1);
|
|
static assert(Intersection!(S1, S2).Seq == AliasSeq!(8, 4));
|
|
|
|
static assert(Intersection!(S1).Seq == AliasSeq!(2, 5, 8, 4));
|
|
static assert(Intersection!().length == 0);
|
|
}
|
|
|
|
/**
|
|
* Produces a $(D_PSYMBOL Set) that contains all elements of
|
|
* $(D_PARAM S1) that are not members of $(D_PARAM S2).
|
|
*
|
|
* Params:
|
|
* S1 = A $(D_PSYMBOL Set).
|
|
* S2 = A $(D_PSYMBOL Set).
|
|
*
|
|
* Returns: Set-theoretic difference of two sets $(D_PARAM S1) and
|
|
* $(D_PARAM S2).
|
|
*
|
|
* See_Also: $(D_PSYMBOL Set).
|
|
*/
|
|
template Difference(alias S1, alias S2)
|
|
if (isInstanceOf!(Set, S1) && isInstanceOf!(Set, S2))
|
|
{
|
|
private template Impl(Args...)
|
|
{
|
|
alias Equal = ApplyLeft!(isEqual, Args[0]);
|
|
enum bool Impl = !anySatisfy!(Equal, S2.Seq);
|
|
}
|
|
|
|
static if (S1.length == 0)
|
|
{
|
|
alias Difference = Set!();
|
|
}
|
|
else static if (S2.length == 1)
|
|
{
|
|
alias Difference = S1;
|
|
}
|
|
else
|
|
{
|
|
alias Difference = Set!(Filter!(Impl, S1.Seq));
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
alias S1 = Set!(2, 5, 8, 4);
|
|
alias S2 = Set!(3, 8, 4, 1);
|
|
static assert(Difference!(S1, S2).Seq == AliasSeq!(2, 5));
|
|
static assert(Difference!(S2, S1).Seq == AliasSeq!(3, 1));
|
|
static assert(Difference!(S1, Set!()).Seq == AliasSeq!(2, 5, 8, 4));
|
|
}
|
|
|
|
/**
|
|
* 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 && __traits(isTemplate, cmp))
|
|
{
|
|
private enum result = cmp!(Args[1], Args[0]);
|
|
static if (is(typeof(result) == bool))
|
|
{
|
|
enum bool isLessEqual = !result;
|
|
}
|
|
else
|
|
{
|
|
enum bool isLessEqual = result >= 0;
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe 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 && __traits(isTemplate, cmp))
|
|
{
|
|
private enum result = cmp!Args;
|
|
static if (is(typeof(result) == bool))
|
|
{
|
|
enum bool isGreaterEqual = !result;
|
|
}
|
|
else
|
|
{
|
|
enum bool isGreaterEqual = result >= 0;
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe 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 && __traits(isTemplate, cmp))
|
|
{
|
|
private enum result = cmp!Args;
|
|
static if (is(typeof(result) == bool))
|
|
{
|
|
enum bool isLess = result;
|
|
}
|
|
else
|
|
{
|
|
enum bool isLess = result < 0;
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe 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 && __traits(isTemplate, cmp))
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe 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.
|
|
*
|
|
* If two items cannot be compared (for example comparing a type with a
|
|
* number), they are considered 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]))
|
|
|| (isTypeTuple!Args && is(Args[0] == Args[1]))
|
|
|| __traits(isSame, Args[0], Args[1]))
|
|
{
|
|
enum bool isEqual = true;
|
|
}
|
|
else
|
|
{
|
|
enum bool isEqual = false;
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
static assert(isEqual!(int, int));
|
|
static assert(isEqual!(8, 8));
|
|
static assert(!isEqual!(int, const(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;
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
static assert(!isNotEqual!(int, int));
|
|
static assert(isNotEqual!(5, int));
|
|
static assert(isNotEqual!(5, 8));
|
|
}
|
|
|
|
/**
|
|
* Looks for $(D_PARAM T) in $(D_PARAM L) and returns $(D_KEYWORD true) if it
|
|
* could be found and $(D_KEYWORD false) otherwise.
|
|
*
|
|
* Params:
|
|
* T = The item to search for.
|
|
* L = Symbol sequence.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) can be found in $(D_PARAM L),
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool canFind(T, L...) = staticIndexOf!(T, L) != -1;
|
|
|
|
/// ditto
|
|
enum bool canFind(alias T, L...) = staticIndexOf!(T, L) != -1;
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
static assert(!canFind!(int));
|
|
static assert(canFind!(int, int));
|
|
static assert(canFind!(int, float, double, int, real));
|
|
static assert(canFind!(3, () {}, uint, 5, 3));
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
static struct S(T)
|
|
{
|
|
}
|
|
static assert(isTemplate!S);
|
|
static assert(!isTemplate!(S!int));
|
|
}
|
|
|
|
/**
|
|
* Attaches a numeric index to each element from $(D_PARAM Args).
|
|
*
|
|
* $(D_PSYMBOL EnumerateFrom) returns a sequence of tuples ($(D_PSYMBOL Pack)s)
|
|
* consisting of the index of each element and the element itself.
|
|
*
|
|
* Params:
|
|
* start = Enumeration initial value.
|
|
* Args = Enumerated sequence.
|
|
*
|
|
* See_Also: $(D_PSYMBOL Enumerate).
|
|
*/
|
|
template EnumerateFrom(size_t start, Args...)
|
|
{
|
|
static if (Args.length == 0)
|
|
{
|
|
alias EnumerateFrom = AliasSeq!();
|
|
}
|
|
else
|
|
{
|
|
alias EnumerateFrom = AliasSeq!(Pack!(start, Args[0]), EnumerateFrom!(start + 1, Args[1 .. $]));
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
static assert(EnumerateFrom!(0, int, uint, bool).length == 3);
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
alias Expected = AliasSeq!(Pack!(cast(size_t) 0, int),
|
|
Pack!(cast(size_t) 1, uint));
|
|
static assert(is(EnumerateFrom!(0, int, uint) == Expected));
|
|
}
|
|
|
|
/**
|
|
* Attaches a numeric index to each element from $(D_PARAM Args).
|
|
*
|
|
* $(D_PSYMBOL EnumerateFrom) returns a sequence of tuples ($(D_PSYMBOL Pack)s)
|
|
* consisting of the index of each element and the element itself.
|
|
*
|
|
* Params:
|
|
* Args = Enumerated sequence.
|
|
*
|
|
* See_Also: $(D_PSYMBOL EnumerateFrom).
|
|
*/
|
|
alias Enumerate(Args...) = EnumerateFrom!(0, Args);
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
alias Expected = AliasSeq!(Pack!(cast(size_t) 0, int),
|
|
Pack!(cast(size_t) 1, uint));
|
|
static assert(is(Enumerate!(int, uint) == Expected));
|
|
}
|