diff options
Diffstat (limited to 'libphobos/src/std/meta.d')
-rw-r--r-- | libphobos/src/std/meta.d | 1679 |
1 files changed, 1679 insertions, 0 deletions
diff --git a/libphobos/src/std/meta.d b/libphobos/src/std/meta.d new file mode 100644 index 0000000..308e50f --- /dev/null +++ b/libphobos/src/std/meta.d @@ -0,0 +1,1679 @@ +// Written in the D programming language. + +/** + * Templates to manipulate template argument lists (also known as type lists). + * + * Some operations on alias sequences are built in to the language, + * such as TL[$(I n)] which gets the $(I n)th type from the + * alias sequence. 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). + * + * $(SCRIPT inhibitQuickIndex = 1;) + * $(DIVC quickindex, + * $(BOOKTABLE , + * $(TR $(TH Category) $(TH Templates)) + * $(TR $(TD Building blocks) $(TD + * $(LREF Alias) + * $(LREF AliasSeq) + * $(LREF aliasSeqOf) + * )) + * $(TR $(TD Alias sequence filtering) $(TD + * $(LREF Erase) + * $(LREF EraseAll) + * $(LREF Filter) + * $(LREF NoDuplicates) + * $(LREF Stride) + * )) + * $(TR $(TD Alias sequence type hierarchy) $(TD + * $(LREF DerivedToFront) + * $(LREF MostDerived) + * )) + * $(TR $(TD Alias sequence transformation) $(TD + * $(LREF Repeat) + * $(LREF Replace) + * $(LREF ReplaceAll) + * $(LREF Reverse) + * $(LREF staticMap) + * $(LREF staticSort) + * )) + * $(TR $(TD Alias sequence searching) $(TD + * $(LREF allSatisfy) + * $(LREF anySatisfy) + * $(LREF staticIndexOf) + * )) + * $(TR $(TD Template predicates) $(TD + * $(LREF templateAnd) + * $(LREF templateNot) + * $(LREF templateOr) + * $(LREF staticIsSorted) + * )) + * $(TR $(TD Template instantiation) $(TD + * $(LREF ApplyLeft) + * $(LREF ApplyRight) + * )) + * )) + * + * 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) + * Copyright: Copyright Digital Mars 2005 - 2015. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: + * $(HTTP digitalmars.com, Walter Bright), + * $(HTTP klickverbot.at, David Nadlinger) + * Source: $(PHOBOSSRC std/_meta.d) + */ + +module std.meta; + +/** + * Creates a sequence of zero or more aliases. This is most commonly + * used as template parameters or arguments. + * + * In previous versions of Phobos, this was known as `TypeTuple`. + */ +template AliasSeq(TList...) +{ + alias AliasSeq = TList; +} + +/// +@safe unittest +{ + import std.meta; + alias TL = AliasSeq!(int, double); + + int foo(TL td) // same as int foo(int, double); + { + return td[0] + cast(int) td[1]; + } +} + +/// +@safe unittest +{ + alias TL = AliasSeq!(int, double); + + alias Types = AliasSeq!(TL, char); + static assert(is(Types == AliasSeq!(int, double, char))); +} + + +/** + Returns an `AliasSeq` expression of `Func` being + applied to every variadic template argument. + */ + +/// +@safe unittest +{ + auto ref ArgCall(alias Func, alias arg)() + { + return Func(arg); + } + + template Map(alias Func, args...) + { + static if (args.length > 1) + { + alias Map = AliasSeq!(ArgCall!(Func, args[0]), Map!(Func, args[1 .. $])); + } + else + { + alias Map = ArgCall!(Func, args[0]); + } + } + + static int square(int arg) + { + return arg * arg; + } + + static int refSquare(ref int arg) + { + arg *= arg; + return arg; + } + + static ref int refRetSquare(ref int arg) + { + arg *= arg; + return arg; + } + + static void test(int a, int b) + { + assert(a == 4); + assert(b == 16); + } + + static void testRef(ref int a, ref int b) + { + assert(a++ == 16); + assert(b++ == 256); + } + + static int a = 2; + static int b = 4; + + test(Map!(square, a, b)); + + test(Map!(refSquare, a, b)); + assert(a == 4); + assert(b == 16); + + testRef(Map!(refRetSquare, a, b)); + assert(a == 17); + assert(b == 257); +} + +/** + * Allows `alias`ing of any single symbol, type or compile-time expression. + * + * Not everything can be directly aliased. An alias cannot be declared + * of - for example - a literal: + * + * `alias a = 4; //Error` + * + * With this template any single entity can be aliased: + * + * `alias b = Alias!4; //OK` + * + * See_Also: + * To alias more than one thing at once, use $(LREF AliasSeq) + */ +alias Alias(alias a) = a; + +/// Ditto +alias Alias(T) = T; + +/// +@safe unittest +{ + // Without Alias this would fail if Args[0] was e.g. a value and + // some logic would be needed to detect when to use enum instead + alias Head(Args ...) = Alias!(Args[0]); + alias Tail(Args ...) = Args[1 .. $]; + + alias Blah = AliasSeq!(3, int, "hello"); + static assert(Head!Blah == 3); + static assert(is(Head!(Tail!Blah) == int)); + static assert((Tail!Blah)[1] == "hello"); +} + +/// +@safe unittest +{ + alias a = Alias!(123); + static assert(a == 123); + + enum abc = 1; + alias b = Alias!(abc); + static assert(b == 1); + + alias c = Alias!(3 + 4); + static assert(c == 7); + + alias concat = (s0, s1) => s0 ~ s1; + alias d = Alias!(concat("Hello", " World!")); + static assert(d == "Hello World!"); + + alias e = Alias!(int); + static assert(is(e == int)); + + alias f = Alias!(AliasSeq!(int)); + static assert(!is(typeof(f[0]))); //not an AliasSeq + static assert(is(f == int)); + + auto g = 6; + alias h = Alias!g; + ++h; + assert(g == 7); +} + +package template OldAlias(alias a) +{ + static if (__traits(compiles, { alias x = a; })) + alias OldAlias = a; + else static if (__traits(compiles, { enum x = a; })) + enum OldAlias = a; + else + static assert(0, "Cannot alias " ~ a.stringof); +} + +import std.traits : isAggregateType, Unqual; + +package template OldAlias(T) +if (!isAggregateType!T || is(Unqual!T == T)) +{ + alias OldAlias = T; +} + +@safe unittest +{ + static struct Foo {} + static assert(is(OldAlias!(const(Foo)) == Foo)); + static assert(is(OldAlias!(const(int)) == const(int))); + static assert(OldAlias!123 == 123); + enum abc = 123; + static assert(OldAlias!abc == 123); +} + +/** + * 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; +} + +/// +@safe unittest +{ + import std.stdio; + + void foo() + { + writefln("The index of long is %s", + staticIndexOf!(long, AliasSeq!(int, long, double))); + // prints: The index of long is 1 + } +} + +// [internal] +private template genericIndexOf(args...) +if (args.length >= 1) +{ + alias e = OldAlias!(args[0]); + alias tuple = args[1 .. $]; + + static if (tuple.length) + { + alias head = OldAlias!(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; + } +} + +@safe 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); +} + +/** + * Returns an `AliasSeq` 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; +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, long, double, char); + alias TL = Erase!(long, Types); + static assert(is(TL == AliasSeq!(int, double, char))); +} + +// [internal] +private template GenericErase(args...) +if (args.length >= 1) +{ + alias e = OldAlias!(args[0]); + alias tuple = args[1 .. $] ; + + static if (tuple.length) + { + alias head = OldAlias!(tuple[0]); + alias tail = tuple[1 .. $]; + + static if (isSame!(e, head)) + alias result = tail; + else + alias result = AliasSeq!(head, GenericErase!(e, tail).result); + } + else + { + alias result = AliasSeq!(); + } +} + +@safe 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 an `AliasSeq` 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; +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, long, long, int); + + alias TL = EraseAll!(long, Types); + static assert(is(TL == AliasSeq!(int, int))); +} + +// [internal] +private template GenericEraseAll(args...) +if (args.length >= 1) +{ + alias e = OldAlias!(args[0]); + alias tuple = args[1 .. $]; + + static if (tuple.length) + { + alias head = OldAlias!(tuple[0]); + alias tail = tuple[1 .. $]; + alias next = AliasSeq!( + GenericEraseAll!(e, tail[0..$/2]).result, + GenericEraseAll!(e, tail[$/2..$]).result + ); + + static if (isSame!(e, head)) + alias result = next; + else + alias result = AliasSeq!(head, next); + } + else + { + alias result = AliasSeq!(); + } +} + +@safe 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 an `AliasSeq` created from TList with the all duplicate + * types removed. + */ +template NoDuplicates(TList...) +{ + template EraseAllN(uint N, T...) + { + static if (N <= 1) + { + alias EraseAllN = T; + } + else + { + alias EraseAllN = EraseAllN!(N-1, T[1 .. N], EraseAll!(T[0], T[N..$])); + } + } + static if (TList.length > 500) + { + enum steps = 16; + alias first = NoDuplicates!(TList[0 .. steps]); + alias NoDuplicates = NoDuplicates!(EraseAllN!(first.length, first, TList[steps..$])); + } + else static if (TList.length == 0) + { + alias NoDuplicates = TList; + } + else + { + alias NoDuplicates = + AliasSeq!(TList[0], NoDuplicates!(EraseAll!(TList[0], TList[1 .. $]))); + } +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, long, long, int, float); + + alias TL = NoDuplicates!(Types); + static assert(is(TL == AliasSeq!(int, long, float))); +} + +@safe unittest +{ + // Bugzilla 14561: huge enums + alias LongList = Repeat!(1500, int); + static assert(NoDuplicates!LongList.length == 1); +} + +@safe unittest +{ + static assert( + Pack!( + NoDuplicates!(1, int, 1, NoDuplicates, int, NoDuplicates, real)) + .equals!(1, int, NoDuplicates, real)); +} + + +/** + * Returns an `AliasSeq` 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; +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, long, long, int, float); + + alias TL = Replace!(long, char, Types); + static assert(is(TL == AliasSeq!(int, char, long, int, float))); +} + +// [internal] +private template GenericReplace(args...) +if (args.length >= 2) +{ + alias from = OldAlias!(args[0]); + alias to = OldAlias!(args[1]); + alias tuple = args[2 .. $]; + + static if (tuple.length) + { + alias head = OldAlias!(tuple[0]); + alias tail = tuple[1 .. $]; + + static if (isSame!(from, head)) + alias result = AliasSeq!(to, tail); + else + alias result = AliasSeq!(head, + GenericReplace!(from, to, tail).result); + } + else + { + alias result = AliasSeq!(); + } + } + +@safe 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 an `AliasSeq` 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; +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, long, long, int, float); + + alias TL = ReplaceAll!(long, char, Types); + static assert(is(TL == AliasSeq!(int, char, char, int, float))); +} + +// [internal] +private template GenericReplaceAll(args...) +if (args.length >= 2) +{ + alias from = OldAlias!(args[0]); + alias to = OldAlias!(args[1]); + alias tuple = args[2 .. $]; + + static if (tuple.length) + { + alias head = OldAlias!(tuple[0]); + alias tail = tuple[1 .. $]; + alias next = GenericReplaceAll!(from, to, tail).result; + + static if (isSame!(from, head)) + alias result = AliasSeq!(to, next); + else + alias result = AliasSeq!(head, next); + } + else + { + alias result = AliasSeq!(); + } +} + +@safe 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 an `AliasSeq` created from TList with the order reversed. + */ +template Reverse(TList...) +{ + static if (TList.length <= 1) + { + alias Reverse = TList; + } + else + { + alias Reverse = + AliasSeq!( + Reverse!(TList[$/2 .. $ ]), + Reverse!(TList[ 0 .. $/2])); + } +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(int, long, long, int, float); + + alias TL = Reverse!(Types); + static assert(is(TL == AliasSeq!(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 .. $]); +} + +/// +@safe unittest +{ + class A { } + class B : A { } + class C : B { } + alias Types = AliasSeq!(A, C, B); + + MostDerived!(Object, Types) x; // x is declared as type C + static assert(is(typeof(x) == C)); +} + +/** + * Returns the `AliasSeq` 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 = + AliasSeq!(MostDerived!(TList[0], TList[1 .. $]), + DerivedToFront!(ReplaceAll!(MostDerived!(TList[0], TList[1 .. $]), + TList[0], + TList[1 .. $]))); +} + +/// +@safe unittest +{ + class A { } + class B : A { } + class C : B { } + alias Types = AliasSeq!(A, C, B); + + alias TL = DerivedToFront!(Types); + static assert(is(TL == AliasSeq!(C, B, A))); +} + +/** +Evaluates to $(D AliasSeq!(F!(T[0]), F!(T[1]), ..., F!(T[$ - 1]))). + */ +template staticMap(alias F, T...) +{ + static if (T.length == 0) + { + alias staticMap = AliasSeq!(); + } + else static if (T.length == 1) + { + alias staticMap = AliasSeq!(F!(T[0])); + } + else + { + alias staticMap = + AliasSeq!( + staticMap!(F, T[ 0 .. $/2]), + staticMap!(F, T[$/2 .. $ ])); + } +} + +/// +@safe unittest +{ + import std.traits : Unqual; + alias TL = staticMap!(Unqual, int, const int, immutable int); + static assert(is(TL == AliasSeq!(int, int, int))); +} + +@safe 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 == AliasSeq!int)); + + alias T = staticMap!(Unqual, int, const int, immutable int); + static assert(is(T == AliasSeq!(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 .. $ ]); + } +} + +/// +@safe 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 short-circuited if a true result is encountered; the +template predicate must be instantiable with one of 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 .. $ ]); + } +} + +/// +@safe unittest +{ + import std.traits : isIntegral; + + static assert(!anySatisfy!(isIntegral, string, double)); + static assert( anySatisfy!(isIntegral, int, double)); +} + + +/** + * Filters an $(D AliasSeq) using a template predicate. Returns a + * $(D AliasSeq) of the elements which satisfy the predicate. + */ +template Filter(alias pred, TList...) +{ + static if (TList.length == 0) + { + alias Filter = AliasSeq!(); + } + else static if (TList.length == 1) + { + static if (pred!(TList[0])) + alias Filter = AliasSeq!(TList[0]); + else + alias Filter = AliasSeq!(); + } + else + { + alias Filter = + AliasSeq!( + Filter!(pred, TList[ 0 .. $/2]), + Filter!(pred, TList[$/2 .. $ ])); + } +} + +/// +@safe unittest +{ + import std.traits : isNarrowString, isUnsigned; + + alias Types1 = AliasSeq!(string, wstring, dchar[], char[], dstring, int); + alias TL1 = Filter!(isNarrowString, Types1); + static assert(is(TL1 == AliasSeq!(string, wstring, char[]))); + + alias Types2 = AliasSeq!(int, byte, ubyte, dstring, dchar, uint, ulong); + alias TL2 = Filter!(isUnsigned, Types2); + static assert(is(TL2 == AliasSeq!(ubyte, uint, ulong))); +} + +@safe unittest +{ + import std.traits : isPointer; + + static assert(is(Filter!(isPointer, int, void*, char[], int*) == AliasSeq!(void*, int*))); + static assert(is(Filter!isPointer == AliasSeq!())); +} + + +// 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; +} + +/// +@safe unittest +{ + import std.traits : isPointer; + + alias isNoPointer = templateNot!isPointer; + static assert(!isNoPointer!(int*)); + static assert(allSatisfy!(isNoPointer, string, char, float)); +} + +@safe unittest +{ + foreach (T; AliasSeq!(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; + } + } +} + +/// +@safe 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); +} + +@safe unittest +{ + foreach (T; AliasSeq!(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); + } + } +} + +/// +@safe 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); +} + +@safe unittest +{ + foreach (T; AliasSeq!(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)))); + } +} + +/** + * Converts an input range $(D range) to an alias sequence. + */ +template aliasSeqOf(alias range) +{ + import std.traits : isArray, isNarrowString; + + alias ArrT = typeof(range); + static if (isArray!ArrT && !isNarrowString!ArrT) + { + static if (range.length == 0) + { + alias aliasSeqOf = AliasSeq!(); + } + else static if (range.length == 1) + { + alias aliasSeqOf = AliasSeq!(range[0]); + } + else + { + alias aliasSeqOf = AliasSeq!(aliasSeqOf!(range[0 .. $/2]), aliasSeqOf!(range[$/2 .. $])); + } + } + else + { + import std.range.primitives : isInputRange; + static if (isInputRange!ArrT) + { + import std.array : array; + alias aliasSeqOf = aliasSeqOf!(array(range)); + } + else + { + static assert(false, "Cannot transform range of type " ~ ArrT.stringof ~ " into a AliasSeq."); + } + } +} + +/// +@safe unittest +{ + import std.algorithm.iteration : map; + import std.algorithm.sorting : sort; + import std.string : capitalize; + + struct S + { + int a; + int c; + int b; + } + + alias capMembers = aliasSeqOf!([__traits(allMembers, S)].sort().map!capitalize()); + static assert(capMembers[0] == "A"); + static assert(capMembers[1] == "B"); + static assert(capMembers[2] == "C"); +} + +/// +@safe unittest +{ + static immutable REF = [0, 1, 2, 3]; + foreach (I, V; aliasSeqOf!([0, 1, 2, 3])) + { + static assert(V == I); + static assert(V == REF[I]); + } +} + +@safe unittest +{ + import std.conv : to, octal; + import std.range : iota; + //Testing compile time octal + foreach (I2; aliasSeqOf!(iota(0, 8))) + foreach (I1; aliasSeqOf!(iota(0, 8))) + { + enum oct = I2 * 8 + I1; + enum dec = I2 * 10 + I1; + enum str = to!string(dec); + static assert(octal!dec == oct); + static assert(octal!str == oct); + } +} + +@safe unittest +{ + enum REF = "日本語"d; + foreach (I, V; aliasSeqOf!"日本語"c) + { + static assert(V == REF[I]); + } +} + +/** + * $(LINK2 http://en.wikipedia.org/wiki/Partial_application, Partially applies) + * $(D_PARAM Template) by binding its first (left) or last (right) arguments + * to $(D_PARAM args). + * + * Behaves like the identity function when $(D_PARAM args) is empty. + * Params: + * Template = template to partially apply + * args = arguments to bind + * Returns: + * _Template with arity smaller than or equal to $(D_PARAM Template) + */ +template ApplyLeft(alias Template, args...) +{ + alias ApplyLeft(right...) = SmartAlias!(Template!(args, right)); +} + +/// Ditto +template ApplyRight(alias Template, args...) +{ + alias ApplyRight(left...) = SmartAlias!(Template!(left, args)); +} + +/// +@safe unittest +{ + // enum bool isImplicitlyConvertible(From, To) + import std.traits : isImplicitlyConvertible; + + static assert(allSatisfy!( + ApplyLeft!(isImplicitlyConvertible, ubyte), + short, ushort, int, uint, long, ulong)); + + static assert(is(Filter!(ApplyRight!(isImplicitlyConvertible, short), + ubyte, string, short, float, int) == AliasSeq!(ubyte, short))); +} + +/// +@safe unittest +{ + import std.traits : hasMember, ifTestable; + + struct T1 + { + bool foo; + } + + struct T2 + { + struct Test + { + bool opCast(T : bool)() { return true; } + } + + Test foo; + } + + static assert(allSatisfy!(ApplyRight!(hasMember, "foo"), T1, T2)); + static assert(allSatisfy!(ApplyRight!(ifTestable, a => a.foo), T1, T2)); +} + +/// +@safe unittest +{ + import std.traits : Largest; + + alias Types = AliasSeq!(byte, short, int, long); + + static assert(is(staticMap!(ApplyLeft!(Largest, short), Types) == + AliasSeq!(short, short, int, long))); + static assert(is(staticMap!(ApplyLeft!(Largest, int), Types) == + AliasSeq!(int, int, int, long))); +} + +/// +@safe unittest +{ + import std.traits : FunctionAttribute, SetFunctionAttributes; + + static void foo() @system; + static int bar(int) @system; + + alias SafeFunctions = AliasSeq!( + void function() @safe, + int function(int) @safe); + + static assert(is(staticMap!(ApplyRight!( + SetFunctionAttributes, "D", FunctionAttribute.safe), + typeof(&foo), typeof(&bar)) == SafeFunctions)); +} + +private template SmartAlias(T...) +{ + static if (T.length == 1) + { + alias SmartAlias = Alias!T; + } + else + { + alias SmartAlias = AliasSeq!T; + } +} + +@safe unittest +{ + static assert(is(typeof({ + alias T(T0, int a, double b, alias T1, string c) = AliasSeq!(T0, a, b, T1, c); + alias T0 = ApplyRight!(ApplyLeft, ApplyRight); + alias T1 = T0!ApplyLeft; + alias T2 = T1!T; + alias T3 = T2!(3, "foo"); + alias T4 = T3!(short, 3, 3.3); + static assert(Pack!T4.equals!(short, 3, 3.3, 3, "foo")); + + import std.traits : isImplicitlyConvertible; + alias U1 = ApplyLeft!(ApplyRight, isImplicitlyConvertible); + alias U2 = U1!int; + enum U3 = U2!short; + static assert(U3); + }))); +} + +/** + * Creates an `AliasSeq` which repeats a type or an `AliasSeq` exactly `n` times. + */ +template Repeat(size_t n, TList...) +if (n > 0) +{ + static if (n == 1) + { + alias Repeat = AliasSeq!TList; + } + else static if (n == 2) + { + alias Repeat = AliasSeq!(TList, TList); + } + else + { + alias R = Repeat!((n - 1) / 2, TList); + static if ((n - 1) % 2 == 0) + { + alias Repeat = AliasSeq!(TList, R, R); + } + else + { + alias Repeat = AliasSeq!(TList, TList, R, R); + } + } +} + +/// +@safe unittest +{ + alias ImInt1 = Repeat!(1, immutable(int)); + static assert(is(ImInt1 == AliasSeq!(immutable(int)))); + + alias Real3 = Repeat!(3, real); + static assert(is(Real3 == AliasSeq!(real, real, real))); + + alias Real12 = Repeat!(4, Real3); + static assert(is(Real12 == AliasSeq!(real, real, real, real, real, real, + real, real, real, real, real, real))); + + alias Composite = AliasSeq!(uint, int); + alias Composite2 = Repeat!(2, Composite); + static assert(is(Composite2 == AliasSeq!(uint, int, uint, int))); +} + + +/// +@safe unittest +{ + auto staticArray(T, size_t n)(Repeat!(n, T) elems) + { + T[n] a = [elems]; + return a; + } + + auto a = staticArray!(long, 3)(3, 1, 4); + assert(is(typeof(a) == long[3])); + assert(a == [3, 1, 4]); +} + +/** + * Sorts a $(LREF AliasSeq) using $(D cmp). + * + * Parameters: + * cmp = A template that returns a $(D bool) (if its first argument is less than the second one) + * or an $(D int) (-1 means less than, 0 means equal, 1 means greater than) + * + * Seq = The $(LREF AliasSeq) to sort + * + * Returns: The sorted alias sequence + */ +template staticSort(alias cmp, Seq...) +{ + static if (Seq.length < 2) + { + alias staticSort = Seq; + } + else + { + private alias btm = staticSort!(cmp, Seq[0 .. $ / 2]); + private alias top = staticSort!(cmp, Seq[$ / 2 .. $]); + + static if (isLessEq!(cmp, btm[$ - 1], top[0])) + alias staticSort = AliasSeq!(btm, top); // already ascending + else static if (isLessEq!(cmp, top[$ - 1], btm[0])) + alias staticSort = AliasSeq!(top, btm); // already descending + else + alias staticSort = staticMerge!(cmp, Seq.length / 2, btm, top); + } +} + +/// +@safe unittest +{ + alias Nums = AliasSeq!(7, 2, 3, 23); + enum Comp(int N1, int N2) = N1 < N2; + static assert(AliasSeq!(2, 3, 7, 23) == staticSort!(Comp, Nums)); +} + +/// +@safe unittest +{ + alias Types = AliasSeq!(uint, short, ubyte, long, ulong); + enum Comp(T1, T2) = __traits(isUnsigned, T2) - __traits(isUnsigned, T1); + static assert(is(AliasSeq!(uint, ubyte, ulong, short, long) == staticSort!(Comp, + Types))); +} + +private template staticMerge(alias cmp, int half, Seq...) +{ + static if (half == 0 || half == Seq.length) + { + alias staticMerge = Seq; + } + else + { + static if (isLessEq!(cmp, Seq[0], Seq[half])) + { + alias staticMerge = AliasSeq!(Seq[0], + staticMerge!(cmp, half - 1, Seq[1 .. $])); + } + else + { + alias staticMerge = AliasSeq!(Seq[half], + staticMerge!(cmp, half, Seq[0 .. half], Seq[half + 1 .. $])); + } + } +} + +private template isLessEq(alias cmp, Seq...) +if (Seq.length == 2) +{ + private enum Result = cmp!(Seq[1], Seq[0]); + static if (is(typeof(Result) == bool)) + enum isLessEq = !Result; + else static if (is(typeof(Result) : int)) + enum isLessEq = Result >= 0; + else + static assert(0, typeof(Result).stringof ~ " is not a value comparison type"); +} + +/** + * Checks if an $(LREF AliasSeq) is sorted according to $(D cmp). + * + * Parameters: + * cmp = A template that returns a $(D bool) (if its first argument is less than the second one) + * or an $(D int) (-1 means less than, 0 means equal, 1 means greater than) + * + * Seq = The $(LREF AliasSeq) to check + * + * Returns: `true` if `Seq` is sorted; otherwise `false` + */ +template staticIsSorted(alias cmp, Seq...) +{ + static if (Seq.length <= 1) + enum staticIsSorted = true; + else static if (Seq.length == 2) + enum staticIsSorted = isLessEq!(cmp, Seq[0], Seq[1]); + else + { + enum staticIsSorted = + isLessEq!(cmp, Seq[($ / 2) - 1], Seq[$ / 2]) && + staticIsSorted!(cmp, Seq[0 .. $ / 2]) && + staticIsSorted!(cmp, Seq[$ / 2 .. $]); + } +} + +/// +@safe unittest +{ + enum Comp(int N1, int N2) = N1 < N2; + static assert( staticIsSorted!(Comp, 2, 2)); + static assert( staticIsSorted!(Comp, 2, 3, 7, 23)); + static assert(!staticIsSorted!(Comp, 7, 2, 3, 23)); +} + +/// +@safe unittest +{ + enum Comp(T1, T2) = __traits(isUnsigned, T2) - __traits(isUnsigned, T1); + static assert( staticIsSorted!(Comp, uint, ubyte, ulong, short, long)); + static assert(!staticIsSorted!(Comp, uint, short, ubyte, long, ulong)); +} + +/** +Selects a subset of the argument list by stepping with fixed `stepSize` over the list. +A negative `stepSize` starts iteration with the last list element. + +Params: + stepSize = Number of elements to increment on each iteration. Can't be `0`. + Args = Template arguments + +Returns: A template argument list filtered by the selected stride. +*/ +template Stride(int stepSize, Args...) +if (stepSize != 0) +{ + static if (Args.length == 0) + { + alias Stride = AliasSeq!(); + } + else static if (stepSize > 0) + { + static if (stepSize >= Args.length) + alias Stride = AliasSeq!(Args[0]); + else + alias Stride = AliasSeq!(Args[0], Stride!(stepSize, Args[stepSize .. $])); + } + else + { + static if (-stepSize >= Args.length) + alias Stride = AliasSeq!(Args[$ - 1]); + else + alias Stride = AliasSeq!(Args[$ - 1], Stride!(stepSize, Args[0 .. $ + stepSize])); + } +} + +/// +@safe unittest +{ + static assert(is(Stride!(1, short, int, long) == AliasSeq!(short, int, long))); + static assert(is(Stride!(2, short, int, long) == AliasSeq!(short, long))); + static assert(is(Stride!(-1, short, int, long) == AliasSeq!(long, int, short))); + static assert(is(Stride!(-2, short, int, long) == AliasSeq!(long, short))); + + alias attribs = AliasSeq!(short, int, long, ushort, uint, ulong); + static assert(is(Stride!(3, attribs) == AliasSeq!(short, ushort))); + static assert(is(Stride!(3, attribs[1 .. $]) == AliasSeq!(int, uint))); + static assert(is(Stride!(-3, attribs) == AliasSeq!(ulong, long))); +} + +@safe unittest +{ + static assert(Pack!(Stride!(5, int)).equals!(int)); + static assert(Pack!(Stride!(-5, int)).equals!(int)); + static assert(!__traits(compiles, Stride!(0, 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) {} + +@safe 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; + } + } +} + +@safe 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 an alias sequence (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; |