phobos/std/typetuple.d
2010-05-18 11:15:44 +00:00

715 lines
17 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.
*
* 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: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
* Authors: $(WEB digitalmars.com, Walter Bright)
*
* 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;
import std.traits;
/**
* Creates a typetuple out of a sequence of zero or more types.
* Example:
* ---
* import std.typetuple;
* alias TypeTuple!(int, double) TL;
*
* int foo(TL td) // same as int foo(int, double);
* {
* return td[0] + cast(int)td[1];
* }
* ---
*
* Example:
* ---
* TypeTuple!(TL, char)
* // is equivalent to:
* TypeTuple!(int, double, char)
* ---
*/
template TypeTuple(TList...)
{
alias TList TypeTuple;
}
/**
* 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.
* Example:
* ---
* import std.typetuple;
* import std.stdio;
*
* void foo()
* {
* writefln("The index of long is ",
* staticIndexOf!(long, TypeTuple!(int, long, double)));
* // prints: The index of long is 1
* }
* ---
*/
template staticIndexOf(T, TList...)
{
enum staticIndexOf = genericIndexOf!(T, TList).index;
}
/// Ditto
template staticIndexOf(alias T, TList...)
{
enum staticIndexOf = genericIndexOf!(T, TList).index;
}
// [internal]
private template genericIndexOf(args...)
if (args.length >= 1)
{
alias Alias!(args[0]) e;
alias args[1 .. $] tuple;
static if (tuple.length)
{
alias Alias!(tuple[0]) head;
alias tuple[1 .. $] tail;
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 staticIndexOf IndexOf;
/**
* Returns a typetuple created from TList with the first occurrence,
* if any, of T removed.
* Example:
* ---
* Erase!(long, int, long, double, char)
* // is the same as:
* TypeTuple!(int, double, char)
* ---
*/
// template Erase(T, TList...)
// {
// static if (TList.length == 0)
// alias TList Erase;
// else static if (is(T == TList[0]))
// alias TList[1 .. $] Erase;
// else
// alias TypeTuple!(TList[0], Erase!(T, TList[1 .. $])) Erase;
// }
template Erase(T, TList...)
{
alias GenericErase!(T, TList).result Erase;
}
/// Ditto
template Erase(alias T, TList...)
{
alias GenericErase!(T, TList).result Erase;
}
// [internal]
private template GenericErase(args...)
if (args.length >= 1)
{
alias Alias!(args[0]) e;
alias args[1 .. $] tuple;
static if (tuple.length)
{
alias Alias!(tuple[0]) head;
alias tuple[1 .. $] tail;
static if (isSame!(e, head))
alias tail result;
else
alias TypeTuple!(head, GenericErase!(e, tail).result) result;
}
else
{
alias TypeTuple!() result;
}
}
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.
* Example:
* ---
* alias TypeTuple!(int, long, long, int) TL;
*
* EraseAll!(long, TL)
* // is the same as:
* TypeTuple!(int, int)
* ---
*/
template EraseAll(T, TList...)
{
alias GenericEraseAll!(T, TList).result EraseAll;
}
/// Ditto
template EraseAll(alias T, TList...)
{
alias GenericEraseAll!(T, TList).result EraseAll;
}
// [internal]
private template GenericEraseAll(args...)
if (args.length >= 1)
{
alias Alias!(args[0]) e;
alias args[1 .. $] tuple;
static if (tuple.length)
{
alias Alias!(tuple[0]) head;
alias tuple[1 .. $] tail;
alias GenericEraseAll!(e, tail).result next;
static if (isSame!(e, head))
alias next result;
else
alias TypeTuple!(head, next) result;
}
else
{
alias TypeTuple!() result;
}
}
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.
* Example:
* ---
* alias TypeTuple!(int, long, long, int, float) TL;
*
* NoDuplicates!(TL)
* // is the same as:
* TypeTuple!(int, long, float)
* ---
*/
template NoDuplicates(TList...)
{
static if (TList.length == 0)
alias TList NoDuplicates;
else
alias TypeTuple!(TList[0], NoDuplicates!(EraseAll!(TList[0], TList[1 .. $]))) NoDuplicates;
}
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.
* Example:
* ---
* alias TypeTuple!(int, long, long, int, float) TL;
*
* Replace!(long, char, TL)
* // is the same as:
* TypeTuple!(int, char, long, int, float)
* ---
*/
template Replace(T, U, TList...)
{
alias GenericReplace!(T, U, TList).result Replace;
}
/// Ditto
template Replace(alias T, U, TList...)
{
alias GenericReplace!(T, U, TList).result Replace;
}
/// Ditto
template Replace(T, alias U, TList...)
{
alias GenericReplace!(T, U, TList).result Replace;
}
/// Ditto
template Replace(alias T, alias U, TList...)
{
alias GenericReplace!(T, U, TList).result Replace;
}
// [internal]
private template GenericReplace(args...)
if (args.length >= 2)
{
alias Alias!(args[0]) from;
alias Alias!(args[1]) to;
alias args[2 .. $] tuple;
static if (tuple.length)
{
alias Alias!(tuple[0]) head;
alias tuple[1 .. $] tail;
static if (isSame!(from, head))
alias TypeTuple!(to, tail) result;
else
alias TypeTuple!(head,
GenericReplace!(from, to, tail).result) result;
}
else
{
alias TypeTuple!() result;
}
}
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.
* Example:
* ---
* alias TypeTuple!(int, long, long, int, float) TL;
*
* ReplaceAll!(long, char, TL)
* // is the same as:
* TypeTuple!(int, char, char, int, float)
* ---
*/
template ReplaceAll(T, U, TList...)
{
alias GenericReplaceAll!(T, U, TList).result ReplaceAll;
}
/// Ditto
template ReplaceAll(alias T, U, TList...)
{
alias GenericReplaceAll!(T, U, TList).result ReplaceAll;
}
/// Ditto
template ReplaceAll(T, alias U, TList...)
{
alias GenericReplaceAll!(T, U, TList).result ReplaceAll;
}
/// Ditto
template ReplaceAll(alias T, alias U, TList...)
{
alias GenericReplaceAll!(T, U, TList).result ReplaceAll;
}
// [internal]
private template GenericReplaceAll(args...)
if (args.length >= 2)
{
alias Alias!(args[0]) from;
alias Alias!(args[1]) to;
alias args[2 .. $] tuple;
static if (tuple.length)
{
alias Alias!(tuple[0]) head;
alias tuple[1 .. $] tail;
alias GenericReplaceAll!(from, to, tail).result next;
static if (isSame!(from, head))
alias TypeTuple!(to, next) result;
else
alias TypeTuple!(head, next) result;
}
else
{
alias TypeTuple!() result;
}
}
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.
* Example:
* ---
* alias TypeTuple!(int, long, long, int, float) TL;
*
* Reverse!(TL)
* // is the same as:
* TypeTuple!(float, int, long, long, int)
* ---
*/
template Reverse(TList...)
{
static if (TList.length == 0)
alias TList Reverse;
else
alias TypeTuple!(Reverse!(TList[1 .. $]), TList[0]) Reverse;
}
/**
* Returns the type from TList that is the most derived from type T.
* If none are found, T is returned.
* Example:
* ---
* class A { }
* class B : A { }
* class C : B { }
* alias TypeTuple!(A, C, B) TL;
*
* MostDerived!(Object, TL) x; // x is declared as type C
* ---
*/
template MostDerived(T, TList...)
{
static if (TList.length == 0)
alias T MostDerived;
else static if (is(TList[0] : T))
alias MostDerived!(TList[0], TList[1 .. $]) MostDerived;
else
alias MostDerived!(T, TList[1 .. $]) MostDerived;
}
/**
* Returns the typetuple TList with the types sorted so that the most
* derived types come first.
* Example:
* ---
* class A { }
* class B : A { }
* class C : B { }
* alias TypeTuple!(A, C, B) TL;
*
* DerivedToFront!(TL)
* // is the same as:
* TypeTuple!(C, B, A)
* ---
*/
template DerivedToFront(TList...)
{
static if (TList.length == 0)
alias TList DerivedToFront;
else
alias TypeTuple!(MostDerived!(TList[0], TList[1 .. $]),
DerivedToFront!(ReplaceAll!(MostDerived!(TList[0], TList[1 .. $]),
TList[0],
TList[1 .. $]))) DerivedToFront;
}
/**
Evaluates to $(D TypeTuple!(F[T[0]], F[T[1]], ..., F[T[$ - 1]])).
Example:
----
alias staticMap!(Unqual, int, const int, immutable int) T;
static assert(is(T == TypeTuple!(int, int, int)));
----
*/
template staticMap(alias F, T...)
{
static if (T.length == 1)
{
alias F!(T[0]) staticMap;
}
else
{
alias TypeTuple!(F!(T[0]), staticMap!(F, T[1 .. $])) staticMap;
}
}
unittest
{
alias staticMap!(Unqual, int, const int, immutable int) T;
static assert(is(T == TypeTuple!(int, int, int)));
}
/**
Evaluates to $(D F[T[0]] && F[T[1]] && ... && F[T[$ - 1]]).
Example:
----
static assert(!allSatisfy!(isIntegral, int, double));
static assert(allSatisfy!(isIntegral, int, long));
----
*/
template allSatisfy(alias F, T...)
{
static if (T.length == 1)
{
alias F!(T[0]) allSatisfy;
}
else
{
enum bool allSatisfy = F!(T[0]) && allSatisfy!(F, T[1 .. $]);
}
}
unittest
{
static assert(!allSatisfy!(isIntegral, int, double));
static assert(allSatisfy!(isIntegral, int, long));
}
// : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : //
private:
/*
* [internal] With the builtin alias declaration, you cannot declare
* aliases of, for example, literal values. You can alias anything
* including literal values via this template.
*/
private
{
// symbols and literal values
template Alias(alias a)
{
static if (__traits(compiles, { alias a x; }))
alias a Alias;
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 a Alias;
}
}
unittest
{
enum abc = 1;
static assert(__traits(compiles, { alias Alias!(123) a; }));
static assert(__traits(compiles, { alias Alias!(abc) a; }));
static assert(__traits(compiles, { alias Alias!(int) a; }));
static assert(__traits(compiles, { alias Alias!(1,abc,int) a; }));
}
/*
* [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, 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) {}
unittest
{
static assert( isSame!(int, int));
static assert(!isSame!(int, short));
enum a = 1, b = 1, c = 2;
static assert( isSame!(1, 1));
static assert( isSame!(a, 1));
static assert( isSame!(a, b));
static assert(!isSame!(b, c));
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));
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));
}
/*
* [internal] Confines a tuple within a template.
*/
private template Pack(T...)
{
alias T tuple;
// 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"));
}