| // 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 |
| } |