mirror of
https://github.com/dlang/phobos.git
synced 2025-04-26 13:10:35 +03:00
1030 lines
26 KiB
D
1030 lines
26 KiB
D
// Written in the D programming language.
|
|
|
|
/**
|
|
* Templates with which to manipulate type tuples (also known as type lists).
|
|
*
|
|
* Some operations on type tuples are built in to the language,
|
|
* such as TL[$(I n)] which gets the $(I n)th type from the
|
|
* type tuple. TL[$(I lwr) .. $(I upr)] returns a new type
|
|
* list that is a slice of the old one.
|
|
*
|
|
* Several templates in this module use or operate on eponymous templates that
|
|
* take a single argument and evaluate to a boolean constant. Such templates
|
|
* are referred to as $(I template predicates).
|
|
*
|
|
* References:
|
|
* Based on ideas in Table 3.1 from
|
|
* $(LINK2 http://amazon.com/exec/obidos/ASIN/0201704315/ref=ase_classicempire/102-2957199-2585768,
|
|
* Modern C++ Design),
|
|
* Andrei Alexandrescu (Addison-Wesley Professional, 2001)
|
|
* Macros:
|
|
* WIKI = Phobos/StdTypeTuple
|
|
*
|
|
* Copyright: Copyright Digital Mars 2005 - 2009.
|
|
* License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
|
|
* Authors:
|
|
* $(WEB digitalmars.com, Walter Bright),
|
|
* $(WEB klickverbot.at, David Nadlinger)
|
|
* Source: $(PHOBOSSRC std/_typetuple.d)
|
|
*/
|
|
/* Copyright Digital Mars 2005 - 2009.
|
|
* Distributed under the Boost Software License, Version 1.0.
|
|
* (See accompanying file LICENSE_1_0.txt or copy at
|
|
* http://www.boost.org/LICENSE_1_0.txt)
|
|
*/
|
|
module std.typetuple;
|
|
|
|
/**
|
|
* Creates a typetuple out of a sequence of zero or more types.
|
|
*/
|
|
template TypeTuple(TList...)
|
|
{
|
|
alias TypeTuple = TList;
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
import std.typetuple;
|
|
alias TL = TypeTuple!(int, double);
|
|
|
|
int foo(TL td) // same as int foo(int, double);
|
|
{
|
|
return td[0] + cast(int)td[1];
|
|
}
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
alias TL = TypeTuple!(int, double);
|
|
|
|
alias Types = TypeTuple!(TL, char);
|
|
static assert(is(Types == TypeTuple!(int, double, char)));
|
|
}
|
|
|
|
/**
|
|
* Returns the index of the first occurrence of type T in the
|
|
* sequence of zero or more types TList.
|
|
* If not found, -1 is returned.
|
|
*/
|
|
template staticIndexOf(T, TList...)
|
|
{
|
|
enum staticIndexOf = genericIndexOf!(T, TList).index;
|
|
}
|
|
|
|
/// Ditto
|
|
template staticIndexOf(alias T, TList...)
|
|
{
|
|
enum staticIndexOf = genericIndexOf!(T, TList).index;
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
import std.typetuple;
|
|
import std.stdio;
|
|
|
|
void foo()
|
|
{
|
|
writefln("The index of long is %s",
|
|
staticIndexOf!(long, TypeTuple!(int, long, double)));
|
|
// prints: The index of long is 1
|
|
}
|
|
}
|
|
|
|
// [internal]
|
|
private template genericIndexOf(args...)
|
|
if (args.length >= 1)
|
|
{
|
|
alias e = Alias!(args[0]);
|
|
alias tuple = args[1 .. $];
|
|
|
|
static if (tuple.length)
|
|
{
|
|
alias head = Alias!(tuple[0]);
|
|
alias tail = tuple[1 .. $];
|
|
|
|
static if (isSame!(e, head))
|
|
{
|
|
enum index = 0;
|
|
}
|
|
else
|
|
{
|
|
enum next = genericIndexOf!(e, tail).index;
|
|
enum index = (next == -1) ? -1 : 1 + next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
enum index = -1;
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
static assert(staticIndexOf!( byte, byte, short, int, long) == 0);
|
|
static assert(staticIndexOf!(short, byte, short, int, long) == 1);
|
|
static assert(staticIndexOf!( int, byte, short, int, long) == 2);
|
|
static assert(staticIndexOf!( long, byte, short, int, long) == 3);
|
|
static assert(staticIndexOf!( char, byte, short, int, long) == -1);
|
|
static assert(staticIndexOf!( -1, byte, short, int, long) == -1);
|
|
static assert(staticIndexOf!(void) == -1);
|
|
|
|
static assert(staticIndexOf!("abc", "abc", "def", "ghi", "jkl") == 0);
|
|
static assert(staticIndexOf!("def", "abc", "def", "ghi", "jkl") == 1);
|
|
static assert(staticIndexOf!("ghi", "abc", "def", "ghi", "jkl") == 2);
|
|
static assert(staticIndexOf!("jkl", "abc", "def", "ghi", "jkl") == 3);
|
|
static assert(staticIndexOf!("mno", "abc", "def", "ghi", "jkl") == -1);
|
|
static assert(staticIndexOf!( void, "abc", "def", "ghi", "jkl") == -1);
|
|
static assert(staticIndexOf!(42) == -1);
|
|
|
|
static assert(staticIndexOf!(void, 0, "void", void) == 2);
|
|
static assert(staticIndexOf!("void", 0, void, "void") == 2);
|
|
}
|
|
|
|
/// Kept for backwards compatibility
|
|
alias IndexOf = staticIndexOf;
|
|
|
|
/**
|
|
* Returns a typetuple created from TList with the first occurrence,
|
|
* if any, of T removed.
|
|
*/
|
|
template Erase(T, TList...)
|
|
{
|
|
alias Erase = GenericErase!(T, TList).result;
|
|
}
|
|
|
|
/// Ditto
|
|
template Erase(alias T, TList...)
|
|
{
|
|
alias Erase = GenericErase!(T, TList).result;
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
alias Types = TypeTuple!(int, long, double, char);
|
|
alias TL = Erase!(long, Types);
|
|
static assert(is(TL == TypeTuple!(int, double, char)));
|
|
}
|
|
|
|
// [internal]
|
|
private template GenericErase(args...)
|
|
if (args.length >= 1)
|
|
{
|
|
alias e = Alias!(args[0]);
|
|
alias tuple = args[1 .. $] ;
|
|
|
|
static if (tuple.length)
|
|
{
|
|
alias head = Alias!(tuple[0]);
|
|
alias tail = tuple[1 .. $];
|
|
|
|
static if (isSame!(e, head))
|
|
alias result = tail;
|
|
else
|
|
alias result = TypeTuple!(head, GenericErase!(e, tail).result);
|
|
}
|
|
else
|
|
{
|
|
alias result = TypeTuple!();
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
static assert(Pack!(Erase!(int,
|
|
short, int, int, 4)).
|
|
equals!(short, int, 4));
|
|
|
|
static assert(Pack!(Erase!(1,
|
|
real, 3, 1, 4, 1, 5, 9)).
|
|
equals!(real, 3, 4, 1, 5, 9));
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a typetuple created from TList with the all occurrences,
|
|
* if any, of T removed.
|
|
*/
|
|
template EraseAll(T, TList...)
|
|
{
|
|
alias EraseAll = GenericEraseAll!(T, TList).result;
|
|
}
|
|
|
|
/// Ditto
|
|
template EraseAll(alias T, TList...)
|
|
{
|
|
alias EraseAll = GenericEraseAll!(T, TList).result;
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
alias Types = TypeTuple!(int, long, long, int);
|
|
|
|
alias TL = EraseAll!(long, Types);
|
|
static assert(is(TL == TypeTuple!(int, int)));
|
|
}
|
|
|
|
// [internal]
|
|
private template GenericEraseAll(args...)
|
|
if (args.length >= 1)
|
|
{
|
|
alias e = Alias!(args[0]);
|
|
alias tuple = args[1 .. $];
|
|
|
|
static if (tuple.length)
|
|
{
|
|
alias head = Alias!(tuple[0]);
|
|
alias tail = tuple[1 .. $];
|
|
alias next = GenericEraseAll!(e, tail).result;
|
|
|
|
static if (isSame!(e, head))
|
|
alias result = next;
|
|
else
|
|
alias result = TypeTuple!(head, next);
|
|
}
|
|
else
|
|
{
|
|
alias result = TypeTuple!();
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
static assert(Pack!(EraseAll!(int,
|
|
short, int, int, 4)).
|
|
equals!(short, 4));
|
|
|
|
static assert(Pack!(EraseAll!(1,
|
|
real, 3, 1, 4, 1, 5, 9)).
|
|
equals!(real, 3, 4, 5, 9));
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a typetuple created from TList with the all duplicate
|
|
* types removed.
|
|
*/
|
|
template NoDuplicates(TList...)
|
|
{
|
|
static if (TList.length == 0)
|
|
alias NoDuplicates = TList;
|
|
else
|
|
alias NoDuplicates =
|
|
TypeTuple!(TList[0], NoDuplicates!(EraseAll!(TList[0], TList[1 .. $])));
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
alias Types = TypeTuple!(int, long, long, int, float);
|
|
|
|
alias TL = NoDuplicates!(Types);
|
|
static assert(is(TL == TypeTuple!(int, long, float)));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
static assert(
|
|
Pack!(
|
|
NoDuplicates!(1, int, 1, NoDuplicates, int, NoDuplicates, real))
|
|
.equals!(1, int, NoDuplicates, real));
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a typetuple created from TList with the first occurrence
|
|
* of type T, if found, replaced with type U.
|
|
*/
|
|
template Replace(T, U, TList...)
|
|
{
|
|
alias Replace = GenericReplace!(T, U, TList).result;
|
|
}
|
|
|
|
/// Ditto
|
|
template Replace(alias T, U, TList...)
|
|
{
|
|
alias Replace = GenericReplace!(T, U, TList).result;
|
|
}
|
|
|
|
/// Ditto
|
|
template Replace(T, alias U, TList...)
|
|
{
|
|
alias Replace = GenericReplace!(T, U, TList).result;
|
|
}
|
|
|
|
/// Ditto
|
|
template Replace(alias T, alias U, TList...)
|
|
{
|
|
alias Replace = GenericReplace!(T, U, TList).result;
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
alias Types = TypeTuple!(int, long, long, int, float);
|
|
|
|
alias TL = Replace!(long, char, Types);
|
|
static assert(is(TL == TypeTuple!(int, char, long, int, float)));
|
|
}
|
|
|
|
// [internal]
|
|
private template GenericReplace(args...)
|
|
if (args.length >= 2)
|
|
{
|
|
alias from = Alias!(args[0]);
|
|
alias to = Alias!(args[1]);
|
|
alias tuple = args[2 .. $];
|
|
|
|
static if (tuple.length)
|
|
{
|
|
alias head = Alias!(tuple[0]);
|
|
alias tail = tuple[1 .. $];
|
|
|
|
static if (isSame!(from, head))
|
|
alias result = TypeTuple!(to, tail);
|
|
else
|
|
alias result = TypeTuple!(head,
|
|
GenericReplace!(from, to, tail).result);
|
|
}
|
|
else
|
|
{
|
|
alias result = TypeTuple!();
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
static assert(Pack!(Replace!(byte, ubyte,
|
|
short, byte, byte, byte)).
|
|
equals!(short, ubyte, byte, byte));
|
|
|
|
static assert(Pack!(Replace!(1111, byte,
|
|
2222, 1111, 1111, 1111)).
|
|
equals!(2222, byte, 1111, 1111));
|
|
|
|
static assert(Pack!(Replace!(byte, 1111,
|
|
short, byte, byte, byte)).
|
|
equals!(short, 1111, byte, byte));
|
|
|
|
static assert(Pack!(Replace!(1111, "11",
|
|
2222, 1111, 1111, 1111)).
|
|
equals!(2222, "11", 1111, 1111));
|
|
}
|
|
|
|
/**
|
|
* Returns a typetuple created from TList with all occurrences
|
|
* of type T, if found, replaced with type U.
|
|
*/
|
|
template ReplaceAll(T, U, TList...)
|
|
{
|
|
alias ReplaceAll = GenericReplaceAll!(T, U, TList).result;
|
|
}
|
|
|
|
/// Ditto
|
|
template ReplaceAll(alias T, U, TList...)
|
|
{
|
|
alias ReplaceAll = GenericReplaceAll!(T, U, TList).result;
|
|
}
|
|
|
|
/// Ditto
|
|
template ReplaceAll(T, alias U, TList...)
|
|
{
|
|
alias ReplaceAll = GenericReplaceAll!(T, U, TList).result;
|
|
}
|
|
|
|
/// Ditto
|
|
template ReplaceAll(alias T, alias U, TList...)
|
|
{
|
|
alias ReplaceAll = GenericReplaceAll!(T, U, TList).result;
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
alias Types = TypeTuple!(int, long, long, int, float);
|
|
|
|
alias TL = ReplaceAll!(long, char, Types);
|
|
static assert(is(TL == TypeTuple!(int, char, char, int, float)));
|
|
}
|
|
|
|
// [internal]
|
|
private template GenericReplaceAll(args...)
|
|
if (args.length >= 2)
|
|
{
|
|
alias from = Alias!(args[0]);
|
|
alias to = Alias!(args[1]);
|
|
alias tuple = args[2 .. $];
|
|
|
|
static if (tuple.length)
|
|
{
|
|
alias head = Alias!(tuple[0]);
|
|
alias tail = tuple[1 .. $];
|
|
alias next = GenericReplaceAll!(from, to, tail).result;
|
|
|
|
static if (isSame!(from, head))
|
|
alias result = TypeTuple!(to, next);
|
|
else
|
|
alias result = TypeTuple!(head, next);
|
|
}
|
|
else
|
|
{
|
|
alias result = TypeTuple!();
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
static assert(Pack!(ReplaceAll!(byte, ubyte,
|
|
byte, short, byte, byte)).
|
|
equals!(ubyte, short, ubyte, ubyte));
|
|
|
|
static assert(Pack!(ReplaceAll!(1111, byte,
|
|
1111, 2222, 1111, 1111)).
|
|
equals!(byte, 2222, byte, byte));
|
|
|
|
static assert(Pack!(ReplaceAll!(byte, 1111,
|
|
byte, short, byte, byte)).
|
|
equals!(1111, short, 1111, 1111));
|
|
|
|
static assert(Pack!(ReplaceAll!(1111, "11",
|
|
1111, 2222, 1111, 1111)).
|
|
equals!("11", 2222, "11", "11"));
|
|
}
|
|
|
|
/**
|
|
* Returns a typetuple created from TList with the order reversed.
|
|
*/
|
|
template Reverse(TList...)
|
|
{
|
|
static if (TList.length <= 1)
|
|
{
|
|
alias Reverse = TList;
|
|
}
|
|
else
|
|
{
|
|
alias Reverse =
|
|
TypeTuple!(
|
|
Reverse!(TList[$/2 .. $ ]),
|
|
Reverse!(TList[ 0 .. $/2]));
|
|
}
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
alias Types = TypeTuple!(int, long, long, int, float);
|
|
|
|
alias TL = Reverse!(Types);
|
|
static assert(is(TL == TypeTuple!(float, int, long, long, int)));
|
|
}
|
|
|
|
/**
|
|
* Returns the type from TList that is the most derived from type T.
|
|
* If none are found, T is returned.
|
|
*/
|
|
template MostDerived(T, TList...)
|
|
{
|
|
static if (TList.length == 0)
|
|
alias MostDerived = T;
|
|
else static if (is(TList[0] : T))
|
|
alias MostDerived = MostDerived!(TList[0], TList[1 .. $]);
|
|
else
|
|
alias MostDerived = MostDerived!(T, TList[1 .. $]);
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
class A { }
|
|
class B : A { }
|
|
class C : B { }
|
|
alias Types = TypeTuple!(A, C, B);
|
|
|
|
MostDerived!(Object, Types) x; // x is declared as type C
|
|
static assert(is(typeof(x) == C));
|
|
}
|
|
|
|
/**
|
|
* Returns the typetuple TList with the types sorted so that the most
|
|
* derived types come first.
|
|
*/
|
|
template DerivedToFront(TList...)
|
|
{
|
|
static if (TList.length == 0)
|
|
alias DerivedToFront = TList;
|
|
else
|
|
alias DerivedToFront =
|
|
TypeTuple!(MostDerived!(TList[0], TList[1 .. $]),
|
|
DerivedToFront!(ReplaceAll!(MostDerived!(TList[0], TList[1 .. $]),
|
|
TList[0],
|
|
TList[1 .. $])));
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
class A { }
|
|
class B : A { }
|
|
class C : B { }
|
|
alias Types = TypeTuple!(A, C, B);
|
|
|
|
alias TL = DerivedToFront!(Types);
|
|
static assert(is(TL == TypeTuple!(C, B, A)));
|
|
}
|
|
|
|
/**
|
|
Evaluates to $(D TypeTuple!(F!(T[0]), F!(T[1]), ..., F!(T[$ - 1]))).
|
|
*/
|
|
template staticMap(alias F, T...)
|
|
{
|
|
static if (T.length == 0)
|
|
{
|
|
alias staticMap = TypeTuple!();
|
|
}
|
|
else static if (T.length == 1)
|
|
{
|
|
alias staticMap = TypeTuple!(F!(T[0]));
|
|
}
|
|
else
|
|
{
|
|
alias staticMap =
|
|
TypeTuple!(
|
|
staticMap!(F, T[ 0 .. $/2]),
|
|
staticMap!(F, T[$/2 .. $ ]));
|
|
}
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
import std.traits : Unqual;
|
|
alias TL = staticMap!(Unqual, int, const int, immutable int);
|
|
static assert(is(TL == TypeTuple!(int, int, int)));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import std.traits : Unqual;
|
|
|
|
// empty
|
|
alias Empty = staticMap!(Unqual);
|
|
static assert(Empty.length == 0);
|
|
|
|
// single
|
|
alias Single = staticMap!(Unqual, const int);
|
|
static assert(is(Single == TypeTuple!int));
|
|
|
|
alias T = staticMap!(Unqual, int, const int, immutable int);
|
|
static assert(is(T == TypeTuple!(int, int, int)));
|
|
}
|
|
|
|
/**
|
|
Tests whether all given items satisfy a template predicate, i.e. evaluates to
|
|
$(D F!(T[0]) && F!(T[1]) && ... && F!(T[$ - 1])).
|
|
|
|
Evaluation is $(I not) short-circuited if a false result is encountered; the
|
|
template predicate must be instantiable with all the given items.
|
|
*/
|
|
template allSatisfy(alias F, T...)
|
|
{
|
|
static if (T.length == 0)
|
|
{
|
|
enum allSatisfy = true;
|
|
}
|
|
else static if (T.length == 1)
|
|
{
|
|
enum allSatisfy = F!(T[0]);
|
|
}
|
|
else
|
|
{
|
|
enum allSatisfy =
|
|
allSatisfy!(F, T[ 0 .. $/2]) &&
|
|
allSatisfy!(F, T[$/2 .. $ ]);
|
|
}
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
import std.traits : isIntegral;
|
|
|
|
static assert(!allSatisfy!(isIntegral, int, double));
|
|
static assert( allSatisfy!(isIntegral, int, long));
|
|
}
|
|
|
|
/**
|
|
Tests whether any given items satisfy a template predicate, i.e. evaluates to
|
|
$(D F!(T[0]) || F!(T[1]) || ... || F!(T[$ - 1])).
|
|
|
|
Evaluation is $(I not) short-circuited if a true result is encountered; the
|
|
template predicate must be instantiable with all the given items.
|
|
*/
|
|
template anySatisfy(alias F, T...)
|
|
{
|
|
static if(T.length == 0)
|
|
{
|
|
enum anySatisfy = false;
|
|
}
|
|
else static if (T.length == 1)
|
|
{
|
|
enum anySatisfy = F!(T[0]);
|
|
}
|
|
else
|
|
{
|
|
enum anySatisfy =
|
|
anySatisfy!(F, T[ 0 .. $/2]) ||
|
|
anySatisfy!(F, T[$/2 .. $ ]);
|
|
}
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
import std.traits : isIntegral;
|
|
|
|
static assert(!anySatisfy!(isIntegral, string, double));
|
|
static assert( anySatisfy!(isIntegral, int, double));
|
|
}
|
|
|
|
|
|
/**
|
|
* Filters a $(D TypeTuple) using a template predicate. Returns a
|
|
* $(D TypeTuple) of the elements which satisfy the predicate.
|
|
*/
|
|
template Filter(alias pred, TList...)
|
|
{
|
|
static if (TList.length == 0)
|
|
{
|
|
alias Filter = TypeTuple!();
|
|
}
|
|
else static if (TList.length == 1)
|
|
{
|
|
static if (pred!(TList[0]))
|
|
alias Filter = TypeTuple!(TList[0]);
|
|
else
|
|
alias Filter = TypeTuple!();
|
|
}
|
|
else
|
|
{
|
|
alias Filter =
|
|
TypeTuple!(
|
|
Filter!(pred, TList[ 0 .. $/2]),
|
|
Filter!(pred, TList[$/2 .. $ ]));
|
|
}
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
import std.traits : isNarrowString, isUnsigned;
|
|
|
|
alias Types1 = TypeTuple!(string, wstring, dchar[], char[], dstring, int);
|
|
alias TL1 = Filter!(isNarrowString, Types1);
|
|
static assert(is(TL1 == TypeTuple!(string, wstring, char[])));
|
|
|
|
alias Types2 = TypeTuple!(int, byte, ubyte, dstring, dchar, uint, ulong);
|
|
alias TL2 = Filter!(isUnsigned, Types2);
|
|
static assert(is(TL2 == TypeTuple!(ubyte, uint, ulong)));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import std.traits : isPointer;
|
|
|
|
static assert(is(Filter!(isPointer, int, void*, char[], int*) == TypeTuple!(void*, int*)));
|
|
static assert(is(Filter!isPointer == TypeTuple!()));
|
|
}
|
|
|
|
|
|
// Used in template predicate unit tests below.
|
|
private version (unittest)
|
|
{
|
|
template testAlways(T...)
|
|
{
|
|
enum testAlways = true;
|
|
}
|
|
|
|
template testNever(T...)
|
|
{
|
|
enum testNever = false;
|
|
}
|
|
|
|
template testError(T...)
|
|
{
|
|
static assert(false, "Should never be instantiated.");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Negates the passed template predicate.
|
|
*/
|
|
template templateNot(alias pred)
|
|
{
|
|
enum templateNot(T...) = !pred!T;
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
import std.traits : isPointer;
|
|
|
|
alias isNoPointer = templateNot!isPointer;
|
|
static assert(!isNoPointer!(int*));
|
|
static assert(allSatisfy!(isNoPointer, string, char, float));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
foreach (T; TypeTuple!(int, staticMap, 42))
|
|
{
|
|
static assert(!Instantiate!(templateNot!testAlways, T));
|
|
static assert(Instantiate!(templateNot!testNever, T));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Combines several template predicates using logical AND, i.e. constructs a new
|
|
* predicate which evaluates to true for a given input T if and only if all of
|
|
* the passed predicates are true for T.
|
|
*
|
|
* The predicates are evaluated from left to right, aborting evaluation in a
|
|
* short-cut manner if a false result is encountered, in which case the latter
|
|
* instantiations do not need to compile.
|
|
*/
|
|
template templateAnd(Preds...)
|
|
{
|
|
template templateAnd(T...)
|
|
{
|
|
static if (Preds.length == 0)
|
|
{
|
|
enum templateAnd = true;
|
|
}
|
|
else
|
|
{
|
|
static if (Instantiate!(Preds[0], T))
|
|
alias templateAnd = Instantiate!(.templateAnd!(Preds[1 .. $]), T);
|
|
else
|
|
enum templateAnd = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
import std.traits : isNumeric, isUnsigned;
|
|
|
|
alias storesNegativeNumbers = templateAnd!(isNumeric, templateNot!isUnsigned);
|
|
static assert(storesNegativeNumbers!int);
|
|
static assert(!storesNegativeNumbers!string && !storesNegativeNumbers!uint);
|
|
|
|
// An empty list of predicates always yields true.
|
|
alias alwaysTrue = templateAnd!();
|
|
static assert(alwaysTrue!int);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
foreach (T; TypeTuple!(int, staticMap, 42))
|
|
{
|
|
static assert( Instantiate!(templateAnd!(), T));
|
|
static assert( Instantiate!(templateAnd!(testAlways), T));
|
|
static assert( Instantiate!(templateAnd!(testAlways, testAlways), T));
|
|
static assert(!Instantiate!(templateAnd!(testNever), T));
|
|
static assert(!Instantiate!(templateAnd!(testAlways, testNever), T));
|
|
static assert(!Instantiate!(templateAnd!(testNever, testAlways), T));
|
|
|
|
static assert(!Instantiate!(templateAnd!(testNever, testError), T));
|
|
static assert(!is(typeof(Instantiate!(templateAnd!(testAlways, testError), T))));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Combines several template predicates using logical OR, i.e. constructs a new
|
|
* predicate which evaluates to true for a given input T if and only at least
|
|
* one of the passed predicates is true for T.
|
|
*
|
|
* The predicates are evaluated from left to right, aborting evaluation in a
|
|
* short-cut manner if a true result is encountered, in which case the latter
|
|
* instantiations do not need to compile.
|
|
*/
|
|
template templateOr(Preds...)
|
|
{
|
|
template templateOr(T...)
|
|
{
|
|
static if (Preds.length == 0)
|
|
{
|
|
enum templateOr = false;
|
|
}
|
|
else
|
|
{
|
|
static if (Instantiate!(Preds[0], T))
|
|
enum templateOr = true;
|
|
else
|
|
alias templateOr = Instantiate!(.templateOr!(Preds[1 .. $]), T);
|
|
}
|
|
}
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
import std.traits : isPointer, isUnsigned;
|
|
|
|
alias isPtrOrUnsigned = templateOr!(isPointer, isUnsigned);
|
|
static assert( isPtrOrUnsigned!uint && isPtrOrUnsigned!(short*));
|
|
static assert(!isPtrOrUnsigned!int && !isPtrOrUnsigned!(string));
|
|
|
|
// An empty list of predicates never yields true.
|
|
alias alwaysFalse = templateOr!();
|
|
static assert(!alwaysFalse!int);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
foreach (T; TypeTuple!(int, staticMap, 42))
|
|
{
|
|
static assert( Instantiate!(templateOr!(testAlways), T));
|
|
static assert( Instantiate!(templateOr!(testAlways, testAlways), T));
|
|
static assert( Instantiate!(templateOr!(testAlways, testNever), T));
|
|
static assert( Instantiate!(templateOr!(testNever, testAlways), T));
|
|
static assert(!Instantiate!(templateOr!(), T));
|
|
static assert(!Instantiate!(templateOr!(testNever), T));
|
|
|
|
static assert( Instantiate!(templateOr!(testAlways, testError), T));
|
|
static assert( Instantiate!(templateOr!(testNever, testAlways, testError), T));
|
|
// DMD @@BUG@@: Assertion fails for int, seems like a error gagging
|
|
// problem. The bug goes away when removing some of the other template
|
|
// instantiations in the module.
|
|
// static assert(!is(typeof(Instantiate!(templateOr!(testNever, testError), T))));
|
|
}
|
|
}
|
|
|
|
|
|
// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : //
|
|
package:
|
|
|
|
/*
|
|
* With the builtin alias declaration, you cannot declare
|
|
* aliases of, for example, literal values. You can alias anything
|
|
* including literal values via this template.
|
|
*/
|
|
// symbols and literal values
|
|
template Alias(alias a)
|
|
{
|
|
static if (__traits(compiles, { alias x = a; }))
|
|
alias Alias = a;
|
|
else static if (__traits(compiles, { enum x = a; }))
|
|
enum Alias = a;
|
|
else
|
|
static assert(0, "Cannot alias " ~ a.stringof);
|
|
}
|
|
// types and tuples
|
|
template Alias(a...)
|
|
{
|
|
alias Alias = a;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
enum abc = 1;
|
|
static assert(__traits(compiles, { alias a = Alias!(123); }));
|
|
static assert(__traits(compiles, { alias a = Alias!(abc); }));
|
|
static assert(__traits(compiles, { alias a = Alias!(int); }));
|
|
static assert(__traits(compiles, { alias a = Alias!(1,abc,int); }));
|
|
}
|
|
|
|
|
|
// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : //
|
|
private:
|
|
|
|
/*
|
|
* [internal] Returns true if a and b are the same thing, or false if
|
|
* not. Both a and b can be types, literals, or symbols.
|
|
*
|
|
* How: When:
|
|
* is(a == b) - both are types
|
|
* a == b - both are literals (true literals, enums)
|
|
* __traits(isSame, a, b) - other cases (variables, functions,
|
|
* templates, etc.)
|
|
*/
|
|
private template isSame(ab...)
|
|
if (ab.length == 2)
|
|
{
|
|
static if (__traits(compiles, expectType!(ab[0]),
|
|
expectType!(ab[1])))
|
|
{
|
|
enum isSame = is(ab[0] == ab[1]);
|
|
}
|
|
else static if (!__traits(compiles, expectType!(ab[0])) &&
|
|
!__traits(compiles, expectType!(ab[1])) &&
|
|
__traits(compiles, expectBool!(ab[0] == ab[1])))
|
|
{
|
|
static if (!__traits(compiles, &ab[0]) ||
|
|
!__traits(compiles, &ab[1]))
|
|
enum isSame = (ab[0] == ab[1]);
|
|
else
|
|
enum isSame = __traits(isSame, ab[0], ab[1]);
|
|
}
|
|
else
|
|
{
|
|
enum isSame = __traits(isSame, ab[0], ab[1]);
|
|
}
|
|
}
|
|
private template expectType(T) {}
|
|
private template expectBool(bool b) {}
|
|
|
|
unittest
|
|
{
|
|
static assert( isSame!(int, int));
|
|
static assert(!isSame!(int, short));
|
|
|
|
enum a = 1, b = 1, c = 2, s = "a", t = "a";
|
|
static assert( isSame!(1, 1));
|
|
static assert( isSame!(a, 1));
|
|
static assert( isSame!(a, b));
|
|
static assert(!isSame!(b, c));
|
|
static assert( isSame!("a", "a"));
|
|
static assert( isSame!(s, "a"));
|
|
static assert( isSame!(s, t));
|
|
static assert(!isSame!(1, "1"));
|
|
static assert(!isSame!(a, "a"));
|
|
static assert( isSame!(isSame, isSame));
|
|
static assert(!isSame!(isSame, a));
|
|
|
|
static assert(!isSame!(byte, a));
|
|
static assert(!isSame!(short, isSame));
|
|
static assert(!isSame!(a, int));
|
|
static assert(!isSame!(long, isSame));
|
|
|
|
static immutable X = 1, Y = 1, Z = 2;
|
|
static assert( isSame!(X, X));
|
|
static assert(!isSame!(X, Y));
|
|
static assert(!isSame!(Y, Z));
|
|
|
|
int foo();
|
|
int bar();
|
|
real baz(int);
|
|
static assert( isSame!(foo, foo));
|
|
static assert(!isSame!(foo, bar));
|
|
static assert(!isSame!(bar, baz));
|
|
static assert( isSame!(baz, baz));
|
|
static assert(!isSame!(foo, 0));
|
|
|
|
int x, y;
|
|
real z;
|
|
static assert( isSame!(x, x));
|
|
static assert(!isSame!(x, y));
|
|
static assert(!isSame!(y, z));
|
|
static assert( isSame!(z, z));
|
|
static assert(!isSame!(x, 0));
|
|
}
|
|
|
|
/*
|
|
* [internal] Confines a tuple within a template.
|
|
*/
|
|
private template Pack(T...)
|
|
{
|
|
alias tuple = T;
|
|
|
|
// For convenience
|
|
template equals(U...)
|
|
{
|
|
static if (T.length == U.length)
|
|
{
|
|
static if (T.length == 0)
|
|
enum equals = true;
|
|
else
|
|
enum equals = isSame!(T[0], U[0]) &&
|
|
Pack!(T[1 .. $]).equals!(U[1 .. $]);
|
|
}
|
|
else
|
|
{
|
|
enum equals = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
static assert( Pack!(1, int, "abc").equals!(1, int, "abc"));
|
|
static assert(!Pack!(1, int, "abc").equals!(1, int, "cba"));
|
|
}
|
|
|
|
/*
|
|
* Instantiates the given template with the given list of parameters.
|
|
*
|
|
* Used to work around syntactic limitations of D with regard to instantiating
|
|
* a template from a type tuple (e.g. T[0]!(...) is not valid) or a template
|
|
* returning another template (e.g. Foo!(Bar)!(Baz) is not allowed).
|
|
*/
|
|
// TODO: Consider publicly exposing this, maybe even if only for better
|
|
// understandability of error messages.
|
|
alias Instantiate(alias Template, Params...) = Template!Params;
|