1
0
Files

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));
}