Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 228 additions & 3 deletions std/typetuple.d
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
* 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,
Expand All @@ -18,7 +22,9 @@
*
* 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)
* Authors:
* $(WEB digitalmars.com, Walter Bright),
* $(WEB klickverbot.at, David Nadlinger)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, do we have a policy on when to add one's name to the Authors list? Other open source projects sometimes require adding to the authors list when adding new top level primitives, so I did, but on the other hand, the additions are hardly very original…

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not aware of any official policy. I generally add my name to anything for which I've made any contributions which seem significant enough (though that's obviously vague and subjective). Some people do the same, while others almost always forget to add their names (e.g. I think that I'm the one who added Kenji's name to std.conv in spite of the fact that he's done a ton of work on it). In general, I'd say that you should add your name when you feel like it's a large enough contribution that it makes sense to have your name on it. If anyone disagrees with you putting your name on a module, they can say so. You've added several functions, so I see no problem with you adding your name to the list. And if they're unoriginal enough that someone can argue that they're not really copyrightable, then oh well. It's Boost, so the license is already about as unrestrictive as a license gets anyway, so I see no reason to get super picky about it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You add names, you add your name to the authors list.

* Source: $(PHOBOSSRC std/_typetuple.d)
*/
/* Copyright Digital Mars 2005 - 2009.
Expand Down Expand Up @@ -553,7 +559,11 @@ unittest
}

/**
Evaluates to $(D F!(T[0]) && F!(T[1]) && ... && F!(T[$ - 1])).
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.

Example:
----
Expand Down Expand Up @@ -584,7 +594,11 @@ unittest
}

/**
Evaluates to $(D F!(T[0]) || F!(T[1]) || ... || F!(T[$ - 1])).
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 true result is encountered; the
template predicate must be instantiable with all the given items.

Example:
----
Expand Down Expand Up @@ -805,3 +819,214 @@ unittest
static assert(is(Filter!(isPointer, int, void*, char[], int*) == TypeTuple!(void*, int*)));
static assert(is(Filter!isPointer == TypeTuple!()));
}


/*
* 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.
template Instantiate(alias Template, Params...)
{
alias Template!Params Instantiate;
}


// Used in template predicate unit tests below.
version (unittest)
{
template always(T...)
{
enum always = true;
}

template never(T...)
{
enum never = false;
}

template error(T...)
{
static assert(false, "Should never be instantiated.");
}
}


/**
* Negates the passed template predicate.
*
* Examples:
* ---
* alias templateNot!isPointer isNoPointer;
* static assert(!isNoPointer!(int*));
* static assert(allSatisfy!(isNoPointer, string, char, float));
* ---
*/
template templateNot(alias pred)
{
template templateNot(T...)
{
enum templateNot = !pred!T;
}
}

// Verify examples.
unittest
{
import std.traits;

alias templateNot!isPointer isNoPointer;
static assert(!isNoPointer!(int*));
static assert(allSatisfy!(isNoPointer, string, char, float));
}

unittest
{
foreach (T; TypeTuple!(int, staticMap, 42))
{
static assert(!Instantiate!(templateNot!always, T));
static assert(Instantiate!(templateNot!never, 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.
*
* Examples:
* ---
* alias templateAnd!(isNumeric, templateNot!isUnsigned) storesNegativeNumbers;
* static assert(storesNegativeNumbers!int);
* static assert(!storesNegativeNumbers!string && !storesNegativeNumbers!uint);
*
* // An empty list of predicates always yields true.
* alias templateAnd!() alwaysTrue;
* static assert(alwaysTrue!int);
* ---
*/
template templateAnd(Preds...)
{
template templateAnd(T...)
{
static if (Preds.length == 0)
{
enum templateAnd = true;
}
else
{
static if (Instantiate!(Preds[0], T))
alias Instantiate!(.templateAnd!(Preds[1 .. $]), T) templateAnd;
else
enum templateAnd = false;
}
}
}

// Verify examples.
unittest
{
alias templateAnd!(isNumeric, templateNot!isUnsigned) storesNegativeNumbers;
static assert(storesNegativeNumbers!int);
static assert(!storesNegativeNumbers!string && !storesNegativeNumbers!uint);

// An empty list of predicates always yields true.
alias templateAnd!() alwaysTrue;
static assert(alwaysTrue!int);
}

unittest
{
foreach (T; TypeTuple!(int, staticMap, 42))
{
static assert(Instantiate!(templateAnd!(), T));
static assert(Instantiate!(templateAnd!(always), T));
static assert(Instantiate!(templateAnd!(always, always), T));
static assert(!Instantiate!(templateAnd!(never), T));
static assert(!Instantiate!(templateAnd!(always, never), T));
static assert(!Instantiate!(templateAnd!(never, always), T));

static assert(!Instantiate!(templateAnd!(never, error), T));
static assert(!is(typeof(Instantiate!(templateAnd!(always, error), 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.
*
* Examples:
* ---
* alias templateOr!(isPointer, isUnsigned) isPtrOrUnsigned;
* static assert(isPtrOrUnsigned!uint && isPtrOrUnsigned!(short*));
* static assert(!isPtrOrUnsigned!int && !isPtrOrUnsigned!string);
*
* // An empty list of predicates never yields true.
* alias templateOr!() alwaysFalse;
* static assert(!alwaysFalse!int);
* ---
*/
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 Instantiate!(.templateOr!(Preds[1 .. $]), T) templateOr;
}
}
}

// Verify examples.
unittest
{
alias templateOr!(isPointer, isUnsigned) isPtrOrUnsigned;
static assert(isPtrOrUnsigned!uint && isPtrOrUnsigned!(short*));
static assert(!isPtrOrUnsigned!int && !isPtrOrUnsigned!string);

// An empty list of predicates never yields true.
alias templateOr!() alwaysFalse;
static assert(!alwaysFalse!int);
}

unittest
{
foreach (T; TypeTuple!(int, staticMap, 42))
{
static assert(Instantiate!(templateOr!(always), T));
static assert(Instantiate!(templateOr!(always, always), T));
static assert(Instantiate!(templateOr!(always, never), T));
static assert(Instantiate!(templateOr!(never, always), T));
static assert(!Instantiate!(templateOr!(), T));
static assert(!Instantiate!(templateOr!(never), T));

static assert(Instantiate!(templateOr!(always, error), T));
static assert(Instantiate!(templateOr!(never, always, error), 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!(never, error), T))));
}
}