// Written in the D programming language. /** * $(RED This module has been deprecated. Use delegates for binding arguments to specific values.) * * Bind function arguments to functions. * * References: * $(LINK2 http://www.boost.org/libs/bind/bind.html, boost bind) * Macros: * WIKI = Phobos/StdBind * * Copyright: Copyright Tomasz Stachowiak 2006 - 2009. * License: Boost License 1.0. * Authors: Tomasz Stachowiak * Source: $(PHOBOSSRC std/_bind.d) */ /* Copyright Tomasz Stachowiak 2006 - 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.bind; deprecated: import std.string : stdFormat = format; import std.traits; import std.typetuple; struct DynArg(int i) { static assert (i >= 0); alias i argNr; } /** When passed to the 'bind' function, they will mark dynamic params - ones that aren't statically bound In boost, they're called __1, __2, __3, etc.. here __0, __1, __2, ... */ const DynArg!(0) _0; const DynArg!(1) _1; /// ditto const DynArg!(2) _2; /// ditto const DynArg!(3) _3; /// ditto const DynArg!(4) _4; /// ditto const DynArg!(5) _5; /// ditto const DynArg!(6) _6; /// ditto const DynArg!(7) _7; /// ditto const DynArg!(8) _8; /// ditto const DynArg!(9) _9; /// ditto /* Detect if a given type is a DynArg of any index */ template isDynArg(T) { static if (is(typeof(T.argNr))) { // must have the argNr field static if(is(T : DynArg!(T.argNr))) { // now check the exact type static enum bool isDynArg = true; } else static enum bool isDynArg = false; } else static enum bool isDynArg = false; } /* Detect if a given type is a DynArg of the specified index */ template isDynArg(T, int i) { static enum bool isDynArg = is(T : DynArg!(i)); } /* Converts a static array type to a dynamic array type */ template DynamicArrayType(T) { alias typeof(T[0])[] DynamicArrayType; } /* Assigns one entity to another. As static arrays don't like normal assignment, slice assignment is used for them. Params: a = destination b = source */ template _assign(T) { static if (isStaticArray!(T)) { void _assign(DynamicArrayType!(T) a, DynamicArrayType!(T) b) { a[] = b[]; } } else { void _assign(ref T a, ref T b) { a = b; } } } /* Assigns and potentially converts one entity to another Normally, only implicit conversion is used, but when both operands are numeric types, an explicit cast is performed on them. Params: T = destination type a = destination Y = source type b = source copyStaticArrays = when a static array is assigned to a dynamic one, it sometimes has to be .dup'ed as the storage may exist in volatile locations */ template _assign(T, Y, bool copyStaticArrays = true) { static if (isStaticArray!(T)) { // if the destination is a static array, copy each element from the source to the destination by a foreach void _assign(DynamicArrayType!(T) a, DynamicArrayType!(Y) b) { foreach (i, x; b) { _assign!(typeof(a[i]), typeof(x))(a[i], x); } } } else static if (!isStaticArray!(T) && isStaticArray!(Y)) { // the destination is a dynamic array and the source is a static array. this sometimes needs a .dup void _assign(ref T a, DynamicArrayType!(Y) b) { static if (copyStaticArrays) { a = b.dup; } else { a = b; } } } else { // none of the items is a static array void _assign(ref T a, ref Y b) { static if (IndexOf!(T, NumericTypes.type) != -1 && IndexOf!(Y, NumericTypes.type) != -1) { a = cast(T)b; } else { a = b; } } } } /** A simple tuple struct with some basic operations */ struct Tuple(T ...) { alias Tuple meta; enum bool expressionTuple = isExpressionTuple!(T); static if (!expressionTuple) { alias T type; // a built-in tuple T value; // a built-in tuple instance } else { alias T value; } enum int length = value.length; /** Statically yields a tuple type with an extra element added at its end */ template appendT(X) { alias .Tuple!(T, X) appendT; } /** Yields a tuple with an extra element added at its end */ appendT!(X) append(X)(X x) { appendT!(X) res; foreach (i, y; value) { _assign!(typeof(y))(res.value[i], y); } _assign!(typeof(x))(res.value[$-1], x); return res; } /** Statically yields a tuple type with an extra element added at its beginning */ template prependT(X) { alias .Tuple!(X, T) prependT; } /** Yields a tuple with an extra element added at its beginning */ prependT!(X) prepend(X)(X x) { prependT!(X) res; foreach (i, y; value) { _assign!(typeof(y))(res.value[i+1], y); } _assign!(typeof(x))(res.value[0], x); return res; } /** Statically concatenates this tuple type with another tuple type */ template concatT(T ...) { static if (expressionTuple) { alias .Tuple!(value, T) concatT; } else { alias .Tuple!(type, T) concatT; } } string toString() { auto res = "(" ~ stdFormat(value[0]); foreach (x; value[1..$]) { res ~= stdFormat(", ", x); } return res ~ ")"; } } /** An empty tuple struct */ struct Tuple() { alias Tuple meta; template EmptyTuple_(T ...) { alias T EmptyTuple_; } alias EmptyTuple_!() type; /// an empty built-in tuple alias EmptyTuple_!() value; /// an empty built-in tuple enum bool expressionTuple = false; enum int length = 0; template appendT(X) { alias .Tuple!(X) appendT; } alias appendT prependT; appendT!(X) append(X)(X x) { appendT!(X) res; foreach (i, y; value) { _assign!(typeof(y))(res.value[i], y); } return res; } alias append prepend; // T - other tuple template concatT(T ...) { alias .Tuple!(T) concatT; } char[] toString() { return "()"; } } /** Dynamically create a tuple from the given items */ Tuple!(T) tuple(T ...)(T t) { Tuple!(T) res; foreach (i, x; t) { _assign!(typeof(x))(res.value[i], x); } return res; } /** Checks whether a given type is the Tuple struct of any length */ template isTypeTuple(T) { static if (is(T.type)) { static if (is(T == Tuple!(T.type))) { enum bool isTypeTuple = true; } else enum bool isTypeTuple = false; } else enum bool isTypeTuple = false; } unittest { static assert(isTypeTuple!(Tuple!(int))); static assert(isTypeTuple!(Tuple!(float, char))); static assert(isTypeTuple!(Tuple!(double, float, int, char[]))); static assert(isTypeTuple!(Tuple!(Object, creal, long))); static assert(!isTypeTuple!(Object)); static assert(!isTypeTuple!(int)); } template minNumArgs_impl(alias fn, fnT) { alias ParameterTypeTuple!(fnT) Params; Params params = void; template loop(int i = 0) { static assert (i <= Params.length); static if (is(typeof(fn(params[0..i])))) { enum int res = i; } else { alias loop!(i+1).res res; } } alias loop!().res res; } /** Finds the minimal number of arguments a given function needs to be provided */ template minNumArgs(alias fn, fnT = typeof(&fn)) { enum int minNumArgs = minNumArgs_impl!(fn, fnT).res; } // mixed into BoundFunc struct/class template MBoundFunc() { // meta alias FAlias_ FAlias; alias FT FuncType; alias AllBoundArgs_ AllBoundArgs; // all arguments given to bind() or bindAlias() static if (!is(typeof(FAlias) == EmptySlot)) { alias Tuple!(ParameterTypeTuple!(FT)) RealFuncParams; // the parameters of the bound function alias FuncReferenceParamsAsPointers!(FAlias) FuncParams; // references converted to pointers } else { alias Tuple!(ParameterTypeTuple!(FT)) FuncParams; // the parameters of the bound function } alias ReturnType!(FT) RetType; // the return type of the bound function alias ExtractedBoundArgs!(AllBoundArgs.type) BoundArgs; // 'saved' arguments. this includes nested/composed functions // if bindAlias was used, we can detect default arguments and only demand the non-default arguments to be specified static if (!is(typeof(FAlias) == EmptySlot)) { enum int minFuncArgs = minNumArgs!(FAlias); alias ParamsPassMethodTuple!(FAlias) ParamPassingMethods; // find out whether the function expects parameters by value or reference } else { enum int minFuncArgs = FuncParams.length; } // the parameters that our wrapper function must get alias getDynArgTypes!(FuncParams, AllBoundArgs, minFuncArgs).res.type DynParams; // data FuncType fp; BoundArgs boundArgs; // yields the number of bound-function parameters that are covered by the binding. takes tuple expansion into account template numFuncArgsReallyBound(int argI = 0, int fargI = 0, int bargI = 0) { // walk though all of AllBoundArgs static if (argI < AllBoundArgs.length) { // the argI-th arg is a composed/nested function static if (isBoundFunc!(AllBoundArgs.type[argI])) { alias DerefFunc!(AllBoundArgs.type[argI]).RetType FuncRetType; enum int argLen = getArgLen!(FuncParams.type[fargI], FuncRetType); enum int bargInc = 1; } // the argI-th arg is a dynamic argument whose value we will get in the call to func() else static if (isDynArg!(AllBoundArgs.type[argI])) { enum int argLen = getArgLen!(FuncParams.type[fargI], DynParams[AllBoundArgs.type[argI].argNr]); enum int bargInc = 0; } // the argI-th arg is a statically bound argument else { enum int argLen = getArgLen!(FuncParams.type[fargI], BoundArgs.type[bargI]); enum int bargInc = 1; } // iterate enum int res = numFuncArgsReallyBound!(argI+1, fargI+argLen, bargI+bargInc).res; } else { // last iteration // the number of bound args is the number of arguments we've detected in this template loop enum int res = fargI; // make sure we'll copy all args the function is going to need static assert (res >= minFuncArgs); } } enum int numSpecifiedParams = numFuncArgsReallyBound!().res; // it's a tuple type whose instance will be applied to the bound function alias Tuple!(FuncParams.type[0 .. numSpecifiedParams]) SpecifiedParams; // argI = indexes AllBoundArgs // fargI = indexes funcArgs // bargI = indexes boundArgs void copyArgs(int argI = 0, int fargI = 0, int bargI = 0)(ref SpecifiedParams funcArgs, DynParams dynArgs) { static if (argI < AllBoundArgs.length) { // the argI-th arg is a composed/nested function static if (isBoundFunc!(AllBoundArgs.type[argI])) { alias DerefFunc!(AllBoundArgs.type[argI]).RetType FuncRetType; alias DerefFunc!(AllBoundArgs.type[argI]).DynParams FuncDynParams; // if FuncDynParams contains an empty slot, e.g. as in the case bind(&f, bind(&g, _1), _0) // then we cannot just apply the dynArgs tuple to the nested/composed function because it will have EmptySlot params // while our dynArgs tuple will contain ordinary types static if (ContainsEmptySlotType!(FuncDynParams)) { FuncDynParams funcParams; // we'll fill it with values in a bit foreach (i, dummy_; dynArgs) { static if (!is(typeof(FuncDynParams[i] == EmptySlot))) { // 3rd param is false because there is no need to .dup static arrays just for the function below this foreach // the storage exists in the whole copyArgs function // dynArgs[i] is used instead of dummy_ so that loop-local data isn't referenced in any dynamic arrays after the loop _assign!(typeof(funcParams[i]), typeof(dummy_), false)(funcParams[i], dynArgs[i]); } } FuncRetType funcRet = boundArgs.value[bargI].func(funcParams); } else { FuncRetType funcRet = boundArgs.value[bargI].func(dynArgs[0..FuncDynParams.length]); // only give it as many dynParams as it needs } // we'll take data from the returned value auto srcItem = &funcRet; enum int bargInc = 1; // nested/composed functions belong to the boundArgs tuple enum bool dupStaticArrays = true; // because the function's return value is stored locally } // the argI-th arg is a dynamic argument whose value we will get in the call to func() else static if (isDynArg!(AllBoundArgs.type[argI])) { // we'll take data from dynArgs auto srcItem = &dynArgs[AllBoundArgs.type[argI].argNr]; enum int bargInc = 0; // dynamic args don't belond to the boundArgs tuple enum bool dupStaticArrays = true; // because we get dynArgs on stack } // the argI-th arg is a statically bound argument else { // we'll take data directly from boundArgs auto srcItem = &boundArgs.value[bargI]; enum int bargInc = 1; // statically bound args belong to the boundArgs tuple enum bool dupStaticArrays = false; // because the storage exists in boundArgs } // the number of bound-function parameters this argument will cover after tuple expansion enum int argLen = getArgLen!(funcArgs.type[fargI], typeof(*srcItem)); static if (isTypeTuple!(typeof(*srcItem)) && !isTypeTuple!(funcArgs.type[fargI])) { foreach (i, x; srcItem.value) { _assign!(funcArgs.type[fargI + i], typeof(x), dupStaticArrays)(funcArgs.value[fargI + i], x); } } else { static assert (1 == argLen); _assign!(funcArgs.type[fargI], typeof(*srcItem), dupStaticArrays)(funcArgs.value[fargI], *srcItem); } // because we might've just expended a tuple, this may be larger than one static assert (argLen >= 1); // we could've just used a dynamic arg (0) or a statically bound arg(1) static assert (bargInc == 0 || bargInc == 1); return copyArgs!(argI+1, fargI+argLen, bargI+bargInc)(funcArgs, dynArgs); } else { // last iteration // make sure we've copied all args the function will need static assert (fargI >= minFuncArgs); } } static if (SpecifiedParams.length > 0) { /// The final wrapped function RetType func(DynParams dynArgs) { SpecifiedParams funcArgs; copyArgs!()(funcArgs, dynArgs); // if the function expects any parameters passed by reference, we'll have to use the ptrApply template // and convert pointers back to references by hand static if (!is(typeof(FAlias) == EmptySlot) && IndexOf!(PassByRef, ParamPassingMethods.type) != -1) { // function parameter type pointers (int, float*, ref char) -> (int*, float*, char*) PointerTuple!(Tuple!(RealFuncParams.type[0 .. SpecifiedParams.length])) ptrs; // initialize the 'ptrs' tuple instance foreach (i, dummy_; funcArgs.value) { static if (is(ParamPassingMethods.type[i] == PassByRef)) { version (BindNoNullCheck) {} else { assert (funcArgs.value[i], "references cannot be null"); } ptrs.value[i] = funcArgs.value[i]; } else { ptrs.value[i] = &funcArgs.value[i]; } } // and call the function :) ptrApply!(RetType, FuncType, ptrs.type)(fp, ptrs.value); } else { // ordinary call-by-tuple return fp(funcArgs.value); } } } else { /// The final wrapped function RetType func() { return fp(); } } /// The final wrapped function alias func call; /// The final wrapped function alias func opCall; /** The type of the delegate that may be returned from this object */ template PtrType() { alias typeof(&(new BoundFunc).call) PtrType; } /** Get a delegate. Equivalent to getting it thru &foo.call */ PtrType!() ptr() { return &this.func; } } version (BindUseStruct) { template DerefFunc(T) { alias typeof(*T) DerefFunc; } /** A context for bound/curried functions */ struct BoundFunc(FT, alias FAlias_, AllBoundArgs_) { mixin MBoundFunc; } } else { template DerefFunc(T) { alias T DerefFunc; } /** A context for bound/curried functions */ class BoundFunc(FT, alias FAlias_, AllBoundArgs_) { mixin MBoundFunc; } } /** bind() can curry or "bind" arguments of a function, producing a different function which requires less parameters, or a different order of parameters. It also allows function composition. The syntax of a bind() call is: bind(function or delegate pointer { , argument }); argument can be one of: