Created
September 3, 2011 22:33
-
-
Save dnadlinger/1191885 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* The term »template predicate« as used below refers to a eponymous template | |
* which (typically) takes a single argument and evaluates to a bool constant. | |
*/ | |
/** | |
* Returns a type tuple containing only the elements of T for which the | |
* eponymous template predicate pred is true. | |
* | |
* Example: | |
* --- | |
* alias StaticFilter!(isIntegral, int, string, long, float[]) Filtered; | |
* static assert(is(Filtered == TypeTuple!(int, long))); | |
* --- | |
*/ | |
template StaticFilter(alias pred, T...) { | |
static if (T.length == 0) { | |
alias TypeTuple!() StaticFilter; | |
} else static if (pred!(T[0])) { | |
alias TypeTuple!(T[0], StaticFilter!(pred, T[1 .. $])) StaticFilter; | |
} else { | |
alias StaticFilter!(pred, T[1 .. $]) StaticFilter; | |
} | |
} | |
/** | |
* Binds the first n arguments of a template to a particular value (where n is | |
* the number of arguments passed to PApply). | |
* | |
* The passed arguments are always applied starting from the left. However, | |
* the special PApplySkip marker template can be used to indicate that an | |
* argument should be skipped, so that e.g. the first and third argument | |
* to a template can be fixed, but the second and remaining arguments would | |
* still be left undefined. | |
* | |
* Skipping a number of parameters, but not providing enough arguments to | |
* assign all of them during instantiation of the resulting template is an | |
* error. | |
* | |
* Example: | |
* --- | |
* struct Foo(T, U, V) {} | |
* alias PApply!(Foo, int, long) PartialFoo; | |
* static assert(is(PartialFoo!float == Foo!(int, long, float))); | |
* | |
* alias PApply!(Test, int, PApplySkip, float) SkippedTest; | |
* static assert(is(SkippedTest!long == Test!(int, long, float))); | |
* --- | |
*/ | |
template PApply(alias Target, T...) { | |
template PApply(U...) { | |
alias Target!(PApplyMergeArgs!(ConfinedTuple!T, U).Result) PApply; | |
} | |
} | |
/// Ditto. | |
template PApplySkip() {} | |
private template PApplyMergeArgs(alias Preset, Args...) { | |
static if (Preset.length == 0) { | |
alias Args Result; | |
} else { | |
enum nextSkip = staticIndexOf!(PApplySkip, Preset.Tuple); | |
static if (nextSkip == -1) { | |
alias TypeTuple!(Preset.Tuple, Args) Result; | |
} else static if (Args.length == 0) { | |
// Have to use a static if clause instead of putting the condition | |
// directly into the assert to avoid DMD trying to access Args[0] | |
// nevertheless below. | |
static assert(false, | |
"PArgsSkip encountered, but no argument left to bind."); | |
} else { | |
alias TypeTuple!( | |
Preset.Tuple[0 .. nextSkip], | |
Args[0], | |
PApplyMergeArgs!( | |
ConfinedTuple!(Preset.Tuple[nextSkip + 1 .. $]), | |
Args[1 .. $] | |
).Result | |
) Result; | |
} | |
} | |
} | |
/** | |
* Composes a number of templates. The result is a template equivalent to | |
* all the passed templates evaluated from right to left, akin to the | |
* mathematical function composition notation: Instantiating Compose!(A, B, C) | |
* is the same as instantiating A!(B!(C!(…))). | |
* | |
* This is especially useful for creating a template to use with staticMap/ | |
* StaticFilter, as demonstrated below. | |
* | |
* Example: | |
* --- | |
* template AllMethodNames(T) { | |
* alias StaticFilter!( | |
* CompilesAndTrue!( | |
* Compose!(isSomeFunction, GetType, PApply!(GetMember, T)) | |
* ), | |
* __traits(allMembers, T) | |
* ) AllMethodNames; | |
* } | |
* | |
* pragma(msg, AllMethodNames!Object); | |
* --- | |
*/ | |
template Compose(T...) { | |
static if (T.length == 0) { | |
template Compose(U...) { | |
alias U Compose; | |
} | |
} else { | |
template Compose(U...) { | |
alias Instantiate!(T[0], Instantiate!(.Compose!(T[1 .. $]), U)) Compose; | |
} | |
} | |
} | |
/** | |
* Instantiates the given template with the given list of parameters. | |
* | |
* Used to work around syntactic limiations 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). | |
*/ | |
template Instantiate(alias Template, Params...) { | |
alias Template!Params Instantiate; | |
} | |
/** | |
* Combines several template predicates using logical AND, i.e. instantiating | |
* All!(a, b, c) with parameters P for some templates a, b, c is equivalent to | |
* a!P && b!P && c!P. | |
* | |
* The templates are evaluated from left to right, aborting evaluation in a | |
* shurt-cut manner if a false result is encountered, in which case the latter | |
* instantiations do not need to compile. | |
*/ | |
template All(T...) { | |
static if (T.length == 0) { | |
template All(U...) { | |
enum All = true; | |
} | |
} else { | |
template All(U...) { | |
static if (Instantiate!(T[0], U)) { | |
alias Instantiate!(.All!(T[1 .. $]), U) All; | |
} else { | |
enum All = false; | |
} | |
} | |
} | |
} | |
/** | |
* Combines several template predicates using logical OR, i.e. instantiating | |
* Any!(a, b, c) with parameters P for some templates a, b, c is equivalent to | |
* a!P || b!P || c!P. | |
* | |
* The templates are evaluated from left to right, aborting evaluation in a | |
* shurt-cut manner if a true result is encountered, in which case the latter | |
* instantiations do not need to compile. | |
*/ | |
template Any(T...) { | |
static if (T.length == 0) { | |
template Any(U...) { | |
enum Any = false; | |
} | |
} else { | |
template Any(U...) { | |
static if (Instantiate!(T[0], U)) { | |
enum Any = true; | |
} else { | |
alias Instantiate!(.Any!(T[1 .. $]), U) Any; | |
} | |
} | |
} | |
} | |
/** | |
* Negates the passed template predicate. | |
*/ | |
template Not(alias T) { | |
template Not(U...) { | |
enum Not = !T!U; | |
} | |
} | |
/** | |
* Wraps the passed template predicate so it returns true if it compiles and | |
* evaluates to true, false it it doesn't compile or evaluates to false. | |
*/ | |
template CompilesAndTrue(alias T) { | |
template CompilesAndTrue(U...) { | |
static if (is(typeof(T!U) : bool)) { | |
enum bool CompilesAndTrue = T!U; | |
} else { | |
enum bool CompilesAndTrue = false; | |
} | |
} | |
} | |
// Copyright: David Nadlinger | |
// License: Boost License 1.0 (http://www.boost.org/LICENSE_1_0.txt) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment