aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/functional.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/src/std/functional.d')
-rw-r--r--libphobos/src/std/functional.d1564
1 files changed, 1564 insertions, 0 deletions
diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d
new file mode 100644
index 0000000..f35d6ff
--- /dev/null
+++ b/libphobos/src/std/functional.d
@@ -0,0 +1,1564 @@
+// Written in the D programming language.
+
+/**
+Functions that manipulate other functions.
+
+This module provides functions for compile time function composition. These
+functions are helpful when constructing predicates for the algorithms in
+$(MREF std, algorithm) or $(MREF std, range).
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE ,
+$(TR $(TH Function Name) $(TH Description)
+)
+ $(TR $(TD $(LREF adjoin))
+ $(TD Joins a couple of functions into one that executes the original
+ functions independently and returns a tuple with all the results.
+ ))
+ $(TR $(TD $(LREF compose), $(LREF pipe))
+ $(TD Join a couple of functions into one that executes the original
+ functions one after the other, using one function's result for the next
+ function's argument.
+ ))
+ $(TR $(TD $(LREF forward))
+ $(TD Forwards function arguments while saving ref-ness.
+ ))
+ $(TR $(TD $(LREF lessThan), $(LREF greaterThan), $(LREF equalTo))
+ $(TD Ready-made predicate functions to compare two values.
+ ))
+ $(TR $(TD $(LREF memoize))
+ $(TD Creates a function that caches its result for fast re-evaluation.
+ ))
+ $(TR $(TD $(LREF not))
+ $(TD Creates a function that negates another.
+ ))
+ $(TR $(TD $(LREF partial))
+ $(TD Creates a function that binds the first argument of a given function
+ to a given value.
+ ))
+ $(TR $(TD $(LREF reverseArgs), $(LREF binaryReverseArgs))
+ $(TD Predicate that reverses the order of its arguments.
+ ))
+ $(TR $(TD $(LREF toDelegate))
+ $(TD Converts a callable to a delegate.
+ ))
+ $(TR $(TD $(LREF unaryFun), $(LREF binaryFun))
+ $(TD Create a unary or binary function from a string. Most often
+ used when defining algorithms on ranges.
+ ))
+)
+
+Copyright: Copyright Andrei Alexandrescu 2008 - 2009.
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP erdani.org, Andrei Alexandrescu)
+Source: $(PHOBOSSRC std/_functional.d)
+*/
+/*
+ Copyright Andrei Alexandrescu 2008 - 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.functional;
+
+import std.meta; // AliasSeq, Reverse
+import std.traits; // isCallable, Parameters
+
+
+private template needOpCallAlias(alias fun)
+{
+ /* Determine whether or not unaryFun and binaryFun need to alias to fun or
+ * fun.opCall. Basically, fun is a function object if fun(...) compiles. We
+ * want is(unaryFun!fun) (resp., is(binaryFun!fun)) to be true if fun is
+ * any function object. There are 4 possible cases:
+ *
+ * 1) fun is the type of a function object with static opCall;
+ * 2) fun is an instance of a function object with static opCall;
+ * 3) fun is the type of a function object with non-static opCall;
+ * 4) fun is an instance of a function object with non-static opCall.
+ *
+ * In case (1), is(unaryFun!fun) should compile, but does not if unaryFun
+ * aliases itself to fun, because typeof(fun) is an error when fun itself
+ * is a type. So it must be aliased to fun.opCall instead. All other cases
+ * should be aliased to fun directly.
+ */
+ static if (is(typeof(fun.opCall) == function))
+ {
+ enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () {
+ return fun(Parameters!fun.init);
+ });
+ }
+ else
+ enum needOpCallAlias = false;
+}
+
+/**
+Transforms a string representing an expression into a unary
+function. The string must either use symbol name $(D a) as
+the parameter or provide the symbol via the $(D parmName) argument.
+If $(D fun) is not a string, $(D unaryFun) aliases itself away to $(D fun).
+*/
+
+template unaryFun(alias fun, string parmName = "a")
+{
+ static if (is(typeof(fun) : string))
+ {
+ static if (!fun._ctfeMatchUnary(parmName))
+ {
+ import std.algorithm, std.conv, std.exception, std.math, std.range, std.string;
+ import std.meta, std.traits, std.typecons;
+ }
+ auto unaryFun(ElementType)(auto ref ElementType __a)
+ {
+ mixin("alias " ~ parmName ~ " = __a ;");
+ return mixin(fun);
+ }
+ }
+ else static if (needOpCallAlias!fun)
+ {
+ // Issue 9906
+ alias unaryFun = fun.opCall;
+ }
+ else
+ {
+ alias unaryFun = fun;
+ }
+}
+
+///
+@safe unittest
+{
+ // Strings are compiled into functions:
+ alias isEven = unaryFun!("(a & 1) == 0");
+ assert(isEven(2) && !isEven(1));
+}
+
+@safe unittest
+{
+ static int f1(int a) { return a + 1; }
+ static assert(is(typeof(unaryFun!(f1)(1)) == int));
+ assert(unaryFun!(f1)(41) == 42);
+ int f2(int a) { return a + 1; }
+ static assert(is(typeof(unaryFun!(f2)(1)) == int));
+ assert(unaryFun!(f2)(41) == 42);
+ assert(unaryFun!("a + 1")(41) == 42);
+ //assert(unaryFun!("return a + 1;")(41) == 42);
+
+ int num = 41;
+ assert(unaryFun!"a + 1"(num) == 42);
+
+ // Issue 9906
+ struct Seen
+ {
+ static bool opCall(int n) { return true; }
+ }
+ static assert(needOpCallAlias!Seen);
+ static assert(is(typeof(unaryFun!Seen(1))));
+ assert(unaryFun!Seen(1));
+
+ Seen s;
+ static assert(!needOpCallAlias!s);
+ static assert(is(typeof(unaryFun!s(1))));
+ assert(unaryFun!s(1));
+
+ struct FuncObj
+ {
+ bool opCall(int n) { return true; }
+ }
+ FuncObj fo;
+ static assert(!needOpCallAlias!fo);
+ static assert(is(typeof(unaryFun!fo)));
+ assert(unaryFun!fo(1));
+
+ // Function object with non-static opCall can only be called with an
+ // instance, not with merely the type.
+ static assert(!is(typeof(unaryFun!FuncObj)));
+}
+
+/**
+Transforms a string representing an expression into a binary function. The
+string must either use symbol names $(D a) and $(D b) as the parameters or
+provide the symbols via the $(D parm1Name) and $(D parm2Name) arguments.
+If $(D fun) is not a string, $(D binaryFun) aliases itself away to
+$(D fun).
+*/
+
+template binaryFun(alias fun, string parm1Name = "a",
+ string parm2Name = "b")
+{
+ static if (is(typeof(fun) : string))
+ {
+ static if (!fun._ctfeMatchBinary(parm1Name, parm2Name))
+ {
+ import std.algorithm, std.conv, std.exception, std.math, std.range, std.string;
+ import std.meta, std.traits, std.typecons;
+ }
+ auto binaryFun(ElementType1, ElementType2)
+ (auto ref ElementType1 __a, auto ref ElementType2 __b)
+ {
+ mixin("alias "~parm1Name~" = __a ;");
+ mixin("alias "~parm2Name~" = __b ;");
+ return mixin(fun);
+ }
+ }
+ else static if (needOpCallAlias!fun)
+ {
+ // Issue 9906
+ alias binaryFun = fun.opCall;
+ }
+ else
+ {
+ alias binaryFun = fun;
+ }
+}
+
+///
+@safe unittest
+{
+ alias less = binaryFun!("a < b");
+ assert(less(1, 2) && !less(2, 1));
+ alias greater = binaryFun!("a > b");
+ assert(!greater("1", "2") && greater("2", "1"));
+}
+
+@safe unittest
+{
+ static int f1(int a, string b) { return a + 1; }
+ static assert(is(typeof(binaryFun!(f1)(1, "2")) == int));
+ assert(binaryFun!(f1)(41, "a") == 42);
+ string f2(int a, string b) { return b ~ "2"; }
+ static assert(is(typeof(binaryFun!(f2)(1, "1")) == string));
+ assert(binaryFun!(f2)(1, "4") == "42");
+ assert(binaryFun!("a + b")(41, 1) == 42);
+ //@@BUG
+ //assert(binaryFun!("return a + b;")(41, 1) == 42);
+
+ // Issue 9906
+ struct Seen
+ {
+ static bool opCall(int x, int y) { return true; }
+ }
+ static assert(is(typeof(binaryFun!Seen)));
+ assert(binaryFun!Seen(1,1));
+
+ struct FuncObj
+ {
+ bool opCall(int x, int y) { return true; }
+ }
+ FuncObj fo;
+ static assert(!needOpCallAlias!fo);
+ static assert(is(typeof(binaryFun!fo)));
+ assert(unaryFun!fo(1,1));
+
+ // Function object with non-static opCall can only be called with an
+ // instance, not with merely the type.
+ static assert(!is(typeof(binaryFun!FuncObj)));
+}
+
+// skip all ASCII chars except a .. z, A .. Z, 0 .. 9, '_' and '.'.
+private uint _ctfeSkipOp(ref string op)
+{
+ if (!__ctfe) assert(false);
+ import std.ascii : isASCII, isAlphaNum;
+ immutable oldLength = op.length;
+ while (op.length)
+ {
+ immutable front = op[0];
+ if (front.isASCII() && !(front.isAlphaNum() || front == '_' || front == '.'))
+ op = op[1..$];
+ else
+ break;
+ }
+ return oldLength != op.length;
+}
+
+// skip all digits
+private uint _ctfeSkipInteger(ref string op)
+{
+ if (!__ctfe) assert(false);
+ import std.ascii : isDigit;
+ immutable oldLength = op.length;
+ while (op.length)
+ {
+ immutable front = op[0];
+ if (front.isDigit())
+ op = op[1..$];
+ else
+ break;
+ }
+ return oldLength != op.length;
+}
+
+// skip name
+private uint _ctfeSkipName(ref string op, string name)
+{
+ if (!__ctfe) assert(false);
+ if (op.length >= name.length && op[0 .. name.length] == name)
+ {
+ op = op[name.length..$];
+ return 1;
+ }
+ return 0;
+}
+
+// returns 1 if $(D fun) is trivial unary function
+private uint _ctfeMatchUnary(string fun, string name)
+{
+ if (!__ctfe) assert(false);
+ fun._ctfeSkipOp();
+ for (;;)
+ {
+ immutable h = fun._ctfeSkipName(name) + fun._ctfeSkipInteger();
+ if (h == 0)
+ {
+ fun._ctfeSkipOp();
+ break;
+ }
+ else if (h == 1)
+ {
+ if (!fun._ctfeSkipOp())
+ break;
+ }
+ else
+ return 0;
+ }
+ return fun.length == 0;
+}
+
+@safe unittest
+{
+ static assert(!_ctfeMatchUnary("sqrt(ё)", "ё"));
+ static assert(!_ctfeMatchUnary("ё.sqrt", "ё"));
+ static assert(!_ctfeMatchUnary(".ё+ё", "ё"));
+ static assert(!_ctfeMatchUnary("_ё+ё", "ё"));
+ static assert(!_ctfeMatchUnary("ёё", "ё"));
+ static assert(_ctfeMatchUnary("a+a", "a"));
+ static assert(_ctfeMatchUnary("a + 10", "a"));
+ static assert(_ctfeMatchUnary("4 == a", "a"));
+ static assert(_ctfeMatchUnary("2 == a", "a"));
+ static assert(_ctfeMatchUnary("1 != a", "a"));
+ static assert(_ctfeMatchUnary("a != 4", "a"));
+ static assert(_ctfeMatchUnary("a< 1", "a"));
+ static assert(_ctfeMatchUnary("434 < a", "a"));
+ static assert(_ctfeMatchUnary("132 > a", "a"));
+ static assert(_ctfeMatchUnary("123 >a", "a"));
+ static assert(_ctfeMatchUnary("a>82", "a"));
+ static assert(_ctfeMatchUnary("ё>82", "ё"));
+ static assert(_ctfeMatchUnary("ё[ё(ё)]", "ё"));
+ static assert(_ctfeMatchUnary("ё[21]", "ё"));
+}
+
+// returns 1 if $(D fun) is trivial binary function
+private uint _ctfeMatchBinary(string fun, string name1, string name2)
+{
+ if (!__ctfe) assert(false);
+ fun._ctfeSkipOp();
+ for (;;)
+ {
+ immutable h = fun._ctfeSkipName(name1) + fun._ctfeSkipName(name2) + fun._ctfeSkipInteger();
+ if (h == 0)
+ {
+ fun._ctfeSkipOp();
+ break;
+ }
+ else if (h == 1)
+ {
+ if (!fun._ctfeSkipOp())
+ break;
+ }
+ else
+ return 0;
+ }
+ return fun.length == 0;
+}
+
+@safe unittest
+{
+
+ static assert(!_ctfeMatchBinary("sqrt(ё)", "ё", "b"));
+ static assert(!_ctfeMatchBinary("ё.sqrt", "ё", "b"));
+ static assert(!_ctfeMatchBinary(".ё+ё", "ё", "b"));
+ static assert(!_ctfeMatchBinary("_ё+ё", "ё", "b"));
+ static assert(!_ctfeMatchBinary("ёё", "ё", "b"));
+ static assert(_ctfeMatchBinary("a+a", "a", "b"));
+ static assert(_ctfeMatchBinary("a + 10", "a", "b"));
+ static assert(_ctfeMatchBinary("4 == a", "a", "b"));
+ static assert(_ctfeMatchBinary("2 == a", "a", "b"));
+ static assert(_ctfeMatchBinary("1 != a", "a", "b"));
+ static assert(_ctfeMatchBinary("a != 4", "a", "b"));
+ static assert(_ctfeMatchBinary("a< 1", "a", "b"));
+ static assert(_ctfeMatchBinary("434 < a", "a", "b"));
+ static assert(_ctfeMatchBinary("132 > a", "a", "b"));
+ static assert(_ctfeMatchBinary("123 >a", "a", "b"));
+ static assert(_ctfeMatchBinary("a>82", "a", "b"));
+ static assert(_ctfeMatchBinary("ё>82", "ё", "q"));
+ static assert(_ctfeMatchBinary("ё[ё(10)]", "ё", "q"));
+ static assert(_ctfeMatchBinary("ё[21]", "ё", "q"));
+
+ static assert(!_ctfeMatchBinary("sqrt(ё)+b", "b", "ё"));
+ static assert(!_ctfeMatchBinary("ё.sqrt-b", "b", "ё"));
+ static assert(!_ctfeMatchBinary(".ё+b", "b", "ё"));
+ static assert(!_ctfeMatchBinary("_b+ё", "b", "ё"));
+ static assert(!_ctfeMatchBinary("ba", "b", "a"));
+ static assert(_ctfeMatchBinary("a+b", "b", "a"));
+ static assert(_ctfeMatchBinary("a + b", "b", "a"));
+ static assert(_ctfeMatchBinary("b == a", "b", "a"));
+ static assert(_ctfeMatchBinary("b == a", "b", "a"));
+ static assert(_ctfeMatchBinary("b != a", "b", "a"));
+ static assert(_ctfeMatchBinary("a != b", "b", "a"));
+ static assert(_ctfeMatchBinary("a< b", "b", "a"));
+ static assert(_ctfeMatchBinary("b < a", "b", "a"));
+ static assert(_ctfeMatchBinary("b > a", "b", "a"));
+ static assert(_ctfeMatchBinary("b >a", "b", "a"));
+ static assert(_ctfeMatchBinary("a>b", "b", "a"));
+ static assert(_ctfeMatchBinary("ё>b", "b", "ё"));
+ static assert(_ctfeMatchBinary("b[ё(-1)]", "b", "ё"));
+ static assert(_ctfeMatchBinary("ё[-21]", "b", "ё"));
+}
+
+//undocumented
+template safeOp(string S)
+if (S=="<"||S==">"||S=="<="||S==">="||S=="=="||S=="!=")
+{
+ import std.traits : isIntegral;
+ private bool unsafeOp(ElementType1, ElementType2)(ElementType1 a, ElementType2 b) pure
+ if (isIntegral!ElementType1 && isIntegral!ElementType2)
+ {
+ import std.traits : CommonType;
+ alias T = CommonType!(ElementType1, ElementType2);
+ return mixin("cast(T)a "~S~" cast(T) b");
+ }
+
+ bool safeOp(T0, T1)(auto ref T0 a, auto ref T1 b)
+ {
+ import std.traits : mostNegative;
+ static if (isIntegral!T0 && isIntegral!T1 &&
+ (mostNegative!T0 < 0) != (mostNegative!T1 < 0))
+ {
+ static if (S == "<=" || S == "<")
+ {
+ static if (mostNegative!T0 < 0)
+ immutable result = a < 0 || unsafeOp(a, b);
+ else
+ immutable result = b >= 0 && unsafeOp(a, b);
+ }
+ else
+ {
+ static if (mostNegative!T0 < 0)
+ immutable result = a >= 0 && unsafeOp(a, b);
+ else
+ immutable result = b < 0 || unsafeOp(a, b);
+ }
+ }
+ else
+ {
+ static assert(is(typeof(mixin("a "~S~" b"))),
+ "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ ".");
+
+ immutable result = mixin("a "~S~" b");
+ }
+ return result;
+ }
+}
+
+@safe unittest //check user defined types
+{
+ import std.algorithm.comparison : equal;
+ struct Foo
+ {
+ int a;
+ auto opEquals(Foo foo)
+ {
+ return a == foo.a;
+ }
+ }
+ assert(safeOp!"!="(Foo(1), Foo(2)));
+}
+
+/**
+ Predicate that returns $(D_PARAM a < b).
+ Correctly compares signed and unsigned integers, ie. -1 < 2U.
+*/
+alias lessThan = safeOp!"<";
+
+///
+pure @safe @nogc nothrow unittest
+{
+ assert(lessThan(2, 3));
+ assert(lessThan(2U, 3U));
+ assert(lessThan(2, 3.0));
+ assert(lessThan(-2, 3U));
+ assert(lessThan(2, 3U));
+ assert(!lessThan(3U, -2));
+ assert(!lessThan(3U, 2));
+ assert(!lessThan(0, 0));
+ assert(!lessThan(0U, 0));
+ assert(!lessThan(0, 0U));
+}
+
+/**
+ Predicate that returns $(D_PARAM a > b).
+ Correctly compares signed and unsigned integers, ie. 2U > -1.
+*/
+alias greaterThan = safeOp!">";
+
+///
+@safe unittest
+{
+ assert(!greaterThan(2, 3));
+ assert(!greaterThan(2U, 3U));
+ assert(!greaterThan(2, 3.0));
+ assert(!greaterThan(-2, 3U));
+ assert(!greaterThan(2, 3U));
+ assert(greaterThan(3U, -2));
+ assert(greaterThan(3U, 2));
+ assert(!greaterThan(0, 0));
+ assert(!greaterThan(0U, 0));
+ assert(!greaterThan(0, 0U));
+}
+
+/**
+ Predicate that returns $(D_PARAM a == b).
+ Correctly compares signed and unsigned integers, ie. !(-1 == ~0U).
+*/
+alias equalTo = safeOp!"==";
+
+///
+@safe unittest
+{
+ assert(equalTo(0U, 0));
+ assert(equalTo(0, 0U));
+ assert(!equalTo(-1, ~0U));
+}
+/**
+ N-ary predicate that reverses the order of arguments, e.g., given
+ $(D pred(a, b, c)), returns $(D pred(c, b, a)).
+*/
+template reverseArgs(alias pred)
+{
+ auto reverseArgs(Args...)(auto ref Args args)
+ if (is(typeof(pred(Reverse!args))))
+ {
+ return pred(Reverse!args);
+ }
+}
+
+///
+@safe unittest
+{
+ alias gt = reverseArgs!(binaryFun!("a < b"));
+ assert(gt(2, 1) && !gt(1, 1));
+ int x = 42;
+ bool xyz(int a, int b) { return a * x < b / x; }
+ auto foo = &xyz;
+ foo(4, 5);
+ alias zyx = reverseArgs!(foo);
+ assert(zyx(5, 4) == foo(4, 5));
+}
+
+///
+@safe unittest
+{
+ int abc(int a, int b, int c) { return a * b + c; }
+ alias cba = reverseArgs!abc;
+ assert(abc(91, 17, 32) == cba(32, 17, 91));
+}
+
+///
+@safe unittest
+{
+ int a(int a) { return a * 2; }
+ alias _a = reverseArgs!a;
+ assert(a(2) == _a(2));
+}
+
+///
+@safe unittest
+{
+ int b() { return 4; }
+ alias _b = reverseArgs!b;
+ assert(b() == _b());
+}
+
+/**
+ Binary predicate that reverses the order of arguments, e.g., given
+ $(D pred(a, b)), returns $(D pred(b, a)).
+*/
+template binaryReverseArgs(alias pred)
+{
+ auto binaryReverseArgs(ElementType1, ElementType2)
+ (auto ref ElementType1 a, auto ref ElementType2 b)
+ {
+ return pred(b, a);
+ }
+}
+
+///
+@safe unittest
+{
+ alias gt = binaryReverseArgs!(binaryFun!("a < b"));
+ assert(gt(2, 1) && !gt(1, 1));
+}
+
+///
+@safe unittest
+{
+ int x = 42;
+ bool xyz(int a, int b) { return a * x < b / x; }
+ auto foo = &xyz;
+ foo(4, 5);
+ alias zyx = binaryReverseArgs!(foo);
+ assert(zyx(5, 4) == foo(4, 5));
+}
+
+/**
+Negates predicate $(D pred).
+ */
+template not(alias pred)
+{
+ auto not(T...)(auto ref T args)
+ {
+ static if (is(typeof(!pred(args))))
+ return !pred(args);
+ else static if (T.length == 1)
+ return !unaryFun!pred(args);
+ else static if (T.length == 2)
+ return !binaryFun!pred(args);
+ else
+ static assert(0);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.searching : find;
+ import std.functional;
+ import std.uni : isWhite;
+ string a = " Hello, world!";
+ assert(find!(not!isWhite)(a) == "Hello, world!");
+}
+
+@safe unittest
+{
+ assert(not!"a != 5"(5));
+ assert(not!"a != b"(5, 5));
+
+ assert(not!(() => false)());
+ assert(not!(a => a != 5)(5));
+ assert(not!((a, b) => a != b)(5, 5));
+ assert(not!((a, b, c) => a * b * c != 125 )(5, 5, 5));
+}
+
+/**
+$(LINK2 http://en.wikipedia.org/wiki/Partial_application, Partially
+applies) $(D_PARAM fun) by tying its first argument to $(D_PARAM arg).
+ */
+template partial(alias fun, alias arg)
+{
+ static if (is(typeof(fun) == delegate) || is(typeof(fun) == function))
+ {
+ import std.traits : ReturnType;
+ ReturnType!fun partial(Parameters!fun[1..$] args2)
+ {
+ return fun(arg, args2);
+ }
+ }
+ else
+ {
+ auto partial(Ts...)(Ts args2)
+ {
+ static if (is(typeof(fun(arg, args2))))
+ {
+ return fun(arg, args2);
+ }
+ else
+ {
+ static string errormsg()
+ {
+ string msg = "Cannot call '" ~ fun.stringof ~ "' with arguments " ~
+ "(" ~ arg.stringof;
+ foreach (T; Ts)
+ msg ~= ", " ~ T.stringof;
+ msg ~= ").";
+ return msg;
+ }
+ static assert(0, errormsg());
+ }
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ int fun(int a, int b) { return a + b; }
+ alias fun5 = partial!(fun, 5);
+ assert(fun5(6) == 11);
+ // Note that in most cases you'd use an alias instead of a value
+ // assignment. Using an alias allows you to partially evaluate template
+ // functions without committing to a particular type of the function.
+}
+
+// tests for partially evaluating callables
+@safe unittest
+{
+ static int f1(int a, int b) { return a + b; }
+ assert(partial!(f1, 5)(6) == 11);
+
+ int f2(int a, int b) { return a + b; }
+ int x = 5;
+ assert(partial!(f2, x)(6) == 11);
+ x = 7;
+ assert(partial!(f2, x)(6) == 13);
+ static assert(partial!(f2, 5)(6) == 11);
+
+ auto dg = &f2;
+ auto f3 = &partial!(dg, x);
+ assert(f3(6) == 13);
+
+ static int funOneArg(int a) { return a; }
+ assert(partial!(funOneArg, 1)() == 1);
+
+ static int funThreeArgs(int a, int b, int c) { return a + b + c; }
+ alias funThreeArgs1 = partial!(funThreeArgs, 1);
+ assert(funThreeArgs1(2, 3) == 6);
+ static assert(!is(typeof(funThreeArgs1(2))));
+
+ enum xe = 5;
+ alias fe = partial!(f2, xe);
+ static assert(fe(6) == 11);
+}
+
+// tests for partially evaluating templated/overloaded callables
+@safe unittest
+{
+ static auto add(A, B)(A x, B y)
+ {
+ return x + y;
+ }
+
+ alias add5 = partial!(add, 5);
+ assert(add5(6) == 11);
+ static assert(!is(typeof(add5())));
+ static assert(!is(typeof(add5(6, 7))));
+
+ // taking address of templated partial evaluation needs explicit type
+ auto dg = &add5!(int);
+ assert(dg(6) == 11);
+
+ int x = 5;
+ alias addX = partial!(add, x);
+ assert(addX(6) == 11);
+
+ static struct Callable
+ {
+ static string opCall(string a, string b) { return a ~ b; }
+ int opCall(int a, int b) { return a * b; }
+ double opCall(double a, double b) { return a + b; }
+ }
+ Callable callable;
+ assert(partial!(Callable, "5")("6") == "56");
+ assert(partial!(callable, 5)(6) == 30);
+ assert(partial!(callable, 7.0)(3.0) == 7.0 + 3.0);
+
+ static struct TCallable
+ {
+ auto opCall(A, B)(A a, B b)
+ {
+ return a + b;
+ }
+ }
+ TCallable tcallable;
+ assert(partial!(tcallable, 5)(6) == 11);
+ static assert(!is(typeof(partial!(tcallable, "5")(6))));
+
+ static A funOneArg(A)(A a) { return a; }
+ alias funOneArg1 = partial!(funOneArg, 1);
+ assert(funOneArg1() == 1);
+
+ static auto funThreeArgs(A, B, C)(A a, B b, C c) { return a + b + c; }
+ alias funThreeArgs1 = partial!(funThreeArgs, 1);
+ assert(funThreeArgs1(2, 3) == 6);
+ static assert(!is(typeof(funThreeArgs1(1))));
+
+ auto dg2 = &funOneArg1!();
+ assert(dg2() == 1);
+}
+
+/**
+Takes multiple functions and adjoins them together. The result is a
+$(REF Tuple, std,typecons) with one element per passed-in function. Upon
+invocation, the returned tuple is the adjoined results of all
+functions.
+
+Note: In the special case where only a single function is provided
+($(D F.length == 1)), adjoin simply aliases to the single passed function
+($(D F[0])).
+*/
+template adjoin(F...)
+if (F.length == 1)
+{
+ alias adjoin = F[0];
+}
+/// ditto
+template adjoin(F...)
+if (F.length > 1)
+{
+ auto adjoin(V...)(auto ref V a)
+ {
+ import std.typecons : tuple;
+ static if (F.length == 2)
+ {
+ return tuple(F[0](a), F[1](a));
+ }
+ else static if (F.length == 3)
+ {
+ return tuple(F[0](a), F[1](a), F[2](a));
+ }
+ else
+ {
+ import std.format : format;
+ import std.range : iota;
+ return mixin (q{tuple(%(F[%s](a)%|, %))}.format(iota(0, F.length)));
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ import std.functional, std.typecons : Tuple;
+ static bool f1(int a) { return a != 0; }
+ static int f2(int a) { return a / 2; }
+ auto x = adjoin!(f1, f2)(5);
+ assert(is(typeof(x) == Tuple!(bool, int)));
+ assert(x[0] == true && x[1] == 2);
+}
+
+@safe unittest
+{
+ import std.typecons : Tuple;
+ static bool F1(int a) { return a != 0; }
+ auto x1 = adjoin!(F1)(5);
+ static int F2(int a) { return a / 2; }
+ auto x2 = adjoin!(F1, F2)(5);
+ assert(is(typeof(x2) == Tuple!(bool, int)));
+ assert(x2[0] && x2[1] == 2);
+ auto x3 = adjoin!(F1, F2, F2)(5);
+ assert(is(typeof(x3) == Tuple!(bool, int, int)));
+ assert(x3[0] && x3[1] == 2 && x3[2] == 2);
+
+ bool F4(int a) { return a != x1; }
+ alias eff4 = adjoin!(F4);
+ static struct S
+ {
+ bool delegate(int) @safe store;
+ int fun() { return 42 + store(5); }
+ }
+ S s;
+ s.store = (int a) { return eff4(a); };
+ auto x4 = s.fun();
+ assert(x4 == 43);
+}
+
+@safe unittest
+{
+ import std.meta : staticMap;
+ import std.typecons : Tuple, tuple;
+ alias funs = staticMap!(unaryFun, "a", "a * 2", "a * 3", "a * a", "-a");
+ alias afun = adjoin!funs;
+ assert(afun(5) == tuple(5, 10, 15, 25, -5));
+
+ static class C{}
+ alias IC = immutable(C);
+ IC foo(){return typeof(return).init;}
+ Tuple!(IC, IC, IC, IC) ret1 = adjoin!(foo, foo, foo, foo)();
+
+ static struct S{int* p;}
+ alias IS = immutable(S);
+ IS bar(){return typeof(return).init;}
+ enum Tuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)();
+}
+
+/**
+ Composes passed-in functions $(D fun[0], fun[1], ...) returning a
+ function $(D f(x)) that in turn returns $(D
+ fun[0](fun[1](...(x)))...). Each function can be a regular
+ functions, a delegate, or a string.
+
+ See_Also: $(LREF pipe)
+*/
+template compose(fun...)
+{
+ static if (fun.length == 1)
+ {
+ alias compose = unaryFun!(fun[0]);
+ }
+ else static if (fun.length == 2)
+ {
+ // starch
+ alias fun0 = unaryFun!(fun[0]);
+ alias fun1 = unaryFun!(fun[1]);
+
+ // protein: the core composition operation
+ typeof({ E a; return fun0(fun1(a)); }()) compose(E)(E a)
+ {
+ return fun0(fun1(a));
+ }
+ }
+ else
+ {
+ // protein: assembling operations
+ alias compose = compose!(fun[0], compose!(fun[1 .. $]));
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+ import std.array : split;
+ import std.conv : to;
+
+ // First split a string in whitespace-separated tokens and then
+ // convert each token into an integer
+ assert(compose!(map!(to!(int)), split)("1 2 3").equal([1, 2, 3]));
+}
+
+/**
+ Pipes functions in sequence. Offers the same functionality as $(D
+ compose), but with functions specified in reverse order. This may
+ lead to more readable code in some situation because the order of
+ execution is the same as lexical order.
+
+ Example:
+
+----
+// Read an entire text file, split the resulting string in
+// whitespace-separated tokens, and then convert each token into an
+// integer
+int[] a = pipe!(readText, split, map!(to!(int)))("file.txt");
+----
+
+ See_Also: $(LREF compose)
+ */
+alias pipe(fun...) = compose!(Reverse!(fun));
+
+@safe unittest
+{
+ import std.conv : to;
+ string foo(int a) { return to!(string)(a); }
+ int bar(string a) { return to!(int)(a) + 1; }
+ double baz(int a) { return a + 0.5; }
+ assert(compose!(baz, bar, foo)(1) == 2.5);
+ assert(pipe!(foo, bar, baz)(1) == 2.5);
+
+ assert(compose!(baz, `to!(int)(a) + 1`, foo)(1) == 2.5);
+ assert(compose!(baz, bar)("1"[]) == 2.5);
+
+ assert(compose!(baz, bar)("1") == 2.5);
+
+ assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5);
+}
+
+/**
+ * $(LINK2 https://en.wikipedia.org/wiki/Memoization, Memoizes) a function so as
+ * to avoid repeated computation. The memoization structure is a hash table keyed by a
+ * tuple of the function's arguments. There is a speed gain if the
+ * function is repeatedly called with the same arguments and is more
+ * expensive than a hash table lookup. For more information on memoization, refer to $(HTTP docs.google.com/viewer?url=http%3A%2F%2Fhop.perl.plover.com%2Fbook%2Fpdf%2F03CachingAndMemoization.pdf, this book chapter).
+
+Example:
+----
+double transmogrify(int a, string b)
+{
+ ... expensive computation ...
+}
+alias fastTransmogrify = memoize!transmogrify;
+unittest
+{
+ auto slow = transmogrify(2, "hello");
+ auto fast = fastTransmogrify(2, "hello");
+ assert(slow == fast);
+}
+----
+
+Technically the memoized function should be pure because $(D memoize) assumes it will
+always return the same result for a given tuple of arguments. However, $(D memoize) does not
+enforce that because sometimes it
+is useful to memoize an impure function, too.
+*/
+template memoize(alias fun)
+{
+ import std.traits : ReturnType;
+ // alias Args = Parameters!fun; // Bugzilla 13580
+
+ ReturnType!fun memoize(Parameters!fun args)
+ {
+ alias Args = Parameters!fun;
+ import std.typecons : Tuple;
+
+ static ReturnType!fun[Tuple!Args] memo;
+ auto t = Tuple!Args(args);
+ if (auto p = t in memo)
+ return *p;
+ return memo[t] = fun(args);
+ }
+}
+
+/// ditto
+template memoize(alias fun, uint maxSize)
+{
+ import std.traits : ReturnType;
+ // alias Args = Parameters!fun; // Bugzilla 13580
+ ReturnType!fun memoize(Parameters!fun args)
+ {
+ import std.traits : hasIndirections;
+ import std.typecons : tuple;
+ static struct Value { Parameters!fun args; ReturnType!fun res; }
+ static Value[] memo;
+ static size_t[] initialized;
+
+ if (!memo.length)
+ {
+ import core.memory : GC;
+
+ // Ensure no allocation overflows
+ static assert(maxSize < size_t.max / Value.sizeof);
+ static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1));
+
+ enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN);
+ memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize];
+ enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
+ initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords];
+ }
+
+ import core.bitop : bt, bts;
+ import std.conv : emplace;
+
+ size_t hash;
+ foreach (ref arg; args)
+ hash = hashOf(arg, hash);
+ // cuckoo hashing
+ immutable idx1 = hash % maxSize;
+ if (!bt(initialized.ptr, idx1))
+ {
+ emplace(&memo[idx1], args, fun(args));
+ bts(initialized.ptr, idx1); // only set to initialized after setting args and value (bugzilla 14025)
+ return memo[idx1].res;
+ }
+ else if (memo[idx1].args == args)
+ return memo[idx1].res;
+ // FNV prime
+ immutable idx2 = (hash * 16_777_619) % maxSize;
+ if (!bt(initialized.ptr, idx2))
+ {
+ emplace(&memo[idx2], memo[idx1]);
+ bts(initialized.ptr, idx2); // only set to initialized after setting args and value (bugzilla 14025)
+ }
+ else if (memo[idx2].args == args)
+ return memo[idx2].res;
+ else if (idx1 != idx2)
+ memo[idx2] = memo[idx1];
+
+ memo[idx1] = Value(args, fun(args));
+ return memo[idx1].res;
+ }
+}
+
+/**
+ * To _memoize a recursive function, simply insert the memoized call in lieu of the plain recursive call.
+ * For example, to transform the exponential-time Fibonacci implementation into a linear-time computation:
+ */
+@safe unittest
+{
+ ulong fib(ulong n) @safe
+ {
+ return n < 2 ? n : memoize!fib(n - 2) + memoize!fib(n - 1);
+ }
+ assert(fib(10) == 55);
+}
+
+/**
+ * To improve the speed of the factorial function,
+ */
+@safe unittest
+{
+ ulong fact(ulong n) @safe
+ {
+ return n < 2 ? 1 : n * memoize!fact(n - 1);
+ }
+ assert(fact(10) == 3628800);
+}
+
+/**
+ * This memoizes all values of $(D fact) up to the largest argument. To only cache the final
+ * result, move $(D memoize) outside the function as shown below.
+ */
+@safe unittest
+{
+ ulong factImpl(ulong n) @safe
+ {
+ return n < 2 ? 1 : n * factImpl(n - 1);
+ }
+ alias fact = memoize!factImpl;
+ assert(fact(10) == 3628800);
+}
+
+/**
+ * When the $(D maxSize) parameter is specified, memoize will used
+ * a fixed size hash table to limit the number of cached entries.
+ */
+@system unittest // not @safe due to memoize
+{
+ ulong fact(ulong n)
+ {
+ // Memoize no more than 8 values
+ return n < 2 ? 1 : n * memoize!(fact, 8)(n - 1);
+ }
+ assert(fact(8) == 40320);
+ // using more entries than maxSize will overwrite existing entries
+ assert(fact(10) == 3628800);
+}
+
+@system unittest // not @safe due to memoize
+{
+ import core.math : sqrt;
+ alias msqrt = memoize!(function double(double x) { return sqrt(x); });
+ auto y = msqrt(2.0);
+ assert(y == msqrt(2.0));
+ y = msqrt(4.0);
+ assert(y == sqrt(4.0));
+
+ // alias mrgb2cmyk = memoize!rgb2cmyk;
+ // auto z = mrgb2cmyk([43, 56, 76]);
+ // assert(z == mrgb2cmyk([43, 56, 76]));
+
+ //alias mfib = memoize!fib;
+
+ static ulong fib(ulong n) @safe
+ {
+ alias mfib = memoize!fib;
+ return n < 2 ? 1 : mfib(n - 2) + mfib(n - 1);
+ }
+
+ auto z = fib(10);
+ assert(z == 89);
+
+ static ulong fact(ulong n) @safe
+ {
+ alias mfact = memoize!fact;
+ return n < 2 ? 1 : n * mfact(n - 1);
+ }
+ assert(fact(10) == 3628800);
+
+ // Issue 12568
+ static uint len2(const string s) { // Error
+ alias mLen2 = memoize!len2;
+ if (s.length == 0)
+ return 0;
+ else
+ return 1 + mLen2(s[1 .. $]);
+ }
+
+ int _func(int x) @safe { return 1; }
+ alias func = memoize!(_func, 10);
+ assert(func(int.init) == 1);
+ assert(func(int.init) == 1);
+}
+
+// 16079: memoize should work with arrays
+@safe unittest
+{
+ int executed = 0;
+ T median(T)(const T[] nums) {
+ import std.algorithm.sorting : sort;
+ executed++;
+ auto arr = nums.dup;
+ arr.sort();
+ if (arr.length % 2)
+ return arr[$ / 2];
+ else
+ return (arr[$ / 2 - 1]
+ + arr[$ / 2]) / 2;
+ }
+
+ alias fastMedian = memoize!(median!int);
+
+ assert(fastMedian([7, 5, 3]) == 5);
+ assert(fastMedian([7, 5, 3]) == 5);
+
+ assert(executed == 1);
+}
+
+// 16079: memoize should work with structs
+@safe unittest
+{
+ int executed = 0;
+ T pickFirst(T)(T first)
+ {
+ executed++;
+ return first;
+ }
+
+ struct Foo { int k; }
+ Foo A = Foo(3);
+
+ alias first = memoize!(pickFirst!Foo);
+ assert(first(Foo(3)) == A);
+ assert(first(Foo(3)) == A);
+ assert(executed == 1);
+}
+
+// 16079: memoize should work with classes
+@safe unittest
+{
+ int executed = 0;
+ T pickFirst(T)(T first)
+ {
+ executed++;
+ return first;
+ }
+
+ class Bar
+ {
+ size_t k;
+ this(size_t k)
+ {
+ this.k = k;
+ }
+ override size_t toHash()
+ {
+ return k;
+ }
+ override bool opEquals(Object o)
+ {
+ auto b = cast(Bar) o;
+ return b && k == b.k;
+ }
+ }
+
+ alias firstClass = memoize!(pickFirst!Bar);
+ assert(firstClass(new Bar(3)).k == 3);
+ assert(firstClass(new Bar(3)).k == 3);
+ assert(executed == 1);
+}
+
+private struct DelegateFaker(F)
+{
+ import std.typecons : FuncInfo, MemberFunctionGenerator;
+
+ // for @safe
+ static F castToF(THIS)(THIS x) @trusted
+ {
+ return cast(F) x;
+ }
+
+ /*
+ * What all the stuff below does is this:
+ *--------------------
+ * struct DelegateFaker(F) {
+ * extern(linkage)
+ * [ref] ReturnType!F doIt(Parameters!F args) [@attributes]
+ * {
+ * auto fp = cast(F) &this;
+ * return fp(args);
+ * }
+ * }
+ *--------------------
+ */
+
+ // We will use MemberFunctionGenerator in std.typecons. This is a policy
+ // configuration for generating the doIt().
+ template GeneratingPolicy()
+ {
+ // Inform the genereator that we only have type information.
+ enum WITHOUT_SYMBOL = true;
+
+ // Generate the function body of doIt().
+ template generateFunctionBody(unused...)
+ {
+ enum generateFunctionBody =
+ // [ref] ReturnType doIt(Parameters args) @attributes
+ q{
+ // When this function gets called, the this pointer isn't
+ // really a this pointer (no instance even really exists), but
+ // a function pointer that points to the function to be called.
+ // Cast it to the correct type and call it.
+
+ auto fp = castToF(&this);
+ return fp(args);
+ };
+ }
+ }
+ // Type information used by the generated code.
+ alias FuncInfo_doIt = FuncInfo!(F);
+
+ // Generate the member function doIt().
+ mixin( MemberFunctionGenerator!(GeneratingPolicy!())
+ .generateFunction!("FuncInfo_doIt", "doIt", F) );
+}
+
+/**
+ * Convert a callable to a delegate with the same parameter list and
+ * return type, avoiding heap allocations and use of auxiliary storage.
+ *
+ * Example:
+ * ----
+ * void doStuff() {
+ * writeln("Hello, world.");
+ * }
+ *
+ * void runDelegate(void delegate() myDelegate) {
+ * myDelegate();
+ * }
+ *
+ * auto delegateToPass = toDelegate(&doStuff);
+ * runDelegate(delegateToPass); // Calls doStuff, prints "Hello, world."
+ * ----
+ *
+ * BUGS:
+ * $(UL
+ * $(LI Does not work with $(D @safe) functions.)
+ * $(LI Ignores C-style / D-style variadic arguments.)
+ * )
+ */
+auto toDelegate(F)(auto ref F fp)
+if (isCallable!(F))
+{
+ static if (is(F == delegate))
+ {
+ return fp;
+ }
+ else static if (is(typeof(&F.opCall) == delegate)
+ || (is(typeof(&F.opCall) V : V*) && is(V == function)))
+ {
+ return toDelegate(&fp.opCall);
+ }
+ else
+ {
+ alias DelType = typeof(&(new DelegateFaker!(F)).doIt);
+
+ static struct DelegateFields {
+ union {
+ DelType del;
+ //pragma(msg, typeof(del));
+
+ struct {
+ void* contextPtr;
+ void* funcPtr;
+ }
+ }
+ }
+
+ // fp is stored in the returned delegate's context pointer.
+ // The returned delegate's function pointer points to
+ // DelegateFaker.doIt.
+ DelegateFields df;
+
+ df.contextPtr = cast(void*) fp;
+
+ DelegateFaker!(F) dummy;
+ auto dummyDel = &dummy.doIt;
+ df.funcPtr = dummyDel.funcptr;
+
+ return df.del;
+ }
+}
+
+///
+@system unittest
+{
+ static int inc(ref uint num) {
+ num++;
+ return 8675309;
+ }
+
+ uint myNum = 0;
+ auto incMyNumDel = toDelegate(&inc);
+ auto returnVal = incMyNumDel(myNum);
+ assert(myNum == 1);
+}
+
+@system unittest // not @safe due to toDelegate
+{
+ static int inc(ref uint num) {
+ num++;
+ return 8675309;
+ }
+
+ uint myNum = 0;
+ auto incMyNumDel = toDelegate(&inc);
+ int delegate(ref uint) dg = incMyNumDel;
+ auto returnVal = incMyNumDel(myNum);
+ assert(myNum == 1);
+
+ interface I { int opCall(); }
+ class C: I { int opCall() { inc(myNum); return myNum;} }
+ auto c = new C;
+ auto i = cast(I) c;
+
+ auto getvalc = toDelegate(c);
+ assert(getvalc() == 2);
+
+ auto getvali = toDelegate(i);
+ assert(getvali() == 3);
+
+ struct S1 { int opCall() { inc(myNum); return myNum; } }
+ static assert(!is(typeof(&s1.opCall) == delegate));
+ S1 s1;
+ auto getvals1 = toDelegate(s1);
+ assert(getvals1() == 4);
+
+ struct S2 { static int opCall() { return 123456; } }
+ static assert(!is(typeof(&S2.opCall) == delegate));
+ S2 s2;
+ auto getvals2 =&S2.opCall;
+ assert(getvals2() == 123456);
+
+ /* test for attributes */
+ {
+ static int refvar = 0xDeadFace;
+
+ static ref int func_ref() { return refvar; }
+ static int func_pure() pure { return 1; }
+ static int func_nothrow() nothrow { return 2; }
+ static int func_property() @property { return 3; }
+ static int func_safe() @safe { return 4; }
+ static int func_trusted() @trusted { return 5; }
+ static int func_system() @system { return 6; }
+ static int func_pure_nothrow() pure nothrow { return 7; }
+ static int func_pure_nothrow_safe() pure nothrow @safe { return 8; }
+
+ auto dg_ref = toDelegate(&func_ref);
+ int delegate() pure dg_pure = toDelegate(&func_pure);
+ int delegate() nothrow dg_nothrow = toDelegate(&func_nothrow);
+ int delegate() @property dg_property = toDelegate(&func_property);
+ int delegate() @safe dg_safe = toDelegate(&func_safe);
+ int delegate() @trusted dg_trusted = toDelegate(&func_trusted);
+ int delegate() @system dg_system = toDelegate(&func_system);
+ int delegate() pure nothrow dg_pure_nothrow = toDelegate(&func_pure_nothrow);
+ int delegate() @safe pure nothrow dg_pure_nothrow_safe = toDelegate(&func_pure_nothrow_safe);
+
+ //static assert(is(typeof(dg_ref) == ref int delegate())); // [BUG@DMD]
+
+ assert(dg_ref() == refvar);
+ assert(dg_pure() == 1);
+ assert(dg_nothrow() == 2);
+ assert(dg_property() == 3);
+ assert(dg_safe() == 4);
+ assert(dg_trusted() == 5);
+ assert(dg_system() == 6);
+ assert(dg_pure_nothrow() == 7);
+ assert(dg_pure_nothrow_safe() == 8);
+ }
+ /* test for linkage */
+ {
+ struct S
+ {
+ extern(C) static void xtrnC() {}
+ extern(D) static void xtrnD() {}
+ }
+ auto dg_xtrnC = toDelegate(&S.xtrnC);
+ auto dg_xtrnD = toDelegate(&S.xtrnD);
+ static assert(! is(typeof(dg_xtrnC) == typeof(dg_xtrnD)));
+ }
+}
+
+/**
+Forwards function arguments with saving ref-ness.
+*/
+template forward(args...)
+{
+ static if (args.length)
+ {
+ import std.algorithm.mutation : move;
+
+ alias arg = args[0];
+ static if (__traits(isRef, arg))
+ alias fwd = arg;
+ else
+ @property fwd()(){ return move(arg); }
+ alias forward = AliasSeq!(fwd, forward!(args[1..$]));
+ }
+ else
+ alias forward = AliasSeq!();
+}
+
+///
+@safe unittest
+{
+ class C
+ {
+ static int foo(int n) { return 1; }
+ static int foo(ref int n) { return 2; }
+ }
+ int bar()(auto ref int x) { return C.foo(forward!x); }
+
+ assert(bar(1) == 1);
+ int i;
+ assert(bar(i) == 2);
+}
+
+///
+@safe unittest
+{
+ void foo(int n, ref string s) { s = null; foreach (i; 0 .. n) s ~= "Hello"; }
+
+ // forwards all arguments which are bound to parameter tuple
+ void bar(Args...)(auto ref Args args) { return foo(forward!args); }
+
+ // forwards all arguments with swapping order
+ void baz(Args...)(auto ref Args args) { return foo(forward!args[$/2..$], forward!args[0..$/2]); }
+
+ string s;
+ bar(1, s);
+ assert(s == "Hello");
+ baz(s, 2);
+ assert(s == "HelloHello");
+}
+
+@safe unittest
+{
+ auto foo(TL...)(auto ref TL args)
+ {
+ string result = "";
+ foreach (i, _; args)
+ {
+ //pragma(msg, "[",i,"] ", __traits(isRef, args[i]) ? "L" : "R");
+ result ~= __traits(isRef, args[i]) ? "L" : "R";
+ }
+ return result;
+ }
+
+ string bar(TL...)(auto ref TL args)
+ {
+ return foo(forward!args);
+ }
+ string baz(TL...)(auto ref TL args)
+ {
+ int x;
+ return foo(forward!args[3], forward!args[2], 1, forward!args[1], forward!args[0], x);
+ }
+
+ struct S {}
+ S makeS(){ return S(); }
+ int n;
+ string s;
+ assert(bar(S(), makeS(), n, s) == "RRLL");
+ assert(baz(S(), makeS(), n, s) == "LLRRRL");
+}
+
+@safe unittest
+{
+ ref int foo(return ref int a) { return a; }
+ ref int bar(Args)(auto ref Args args)
+ {
+ return foo(forward!args);
+ }
+ static assert(!__traits(compiles, { auto x1 = bar(3); })); // case of NG
+ int value = 3;
+ auto x2 = bar(value); // case of OK
+}