Skip to content

Instantly share code, notes, and snippets.

@dnadlinger
Created September 3, 2011 22:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dnadlinger/1191885 to your computer and use it in GitHub Desktop.
Save dnadlinger/1191885 to your computer and use it in GitHub Desktop.
/*
* 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