www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Generic method that takes in either delegate or function

reply %u <wfunction hotmail.com> writes:
Hi,

Is there any way to specify a parameter as "something that can be called with
parameter types A, B, C and that returns a value of type D", without caring
whether it's a delegate, a function, or an object that overloads opCall? (This
might require the use of templates, but I still can't figure it out...)

Thank you!
Jan 12 2011
next sibling parent Stanislav Blinov <stanislav.blinov gmail.com> writes:
On 01/12/2011 11:21 AM, %u wrote:
 Hi,

 Is there any way to specify a parameter as "something that can be called with
 parameter types A, B, C and that returns a value of type D", without caring
 whether it's a delegate, a function, or an object that overloads opCall? (This
 might require the use of templates, but I still can't figure it out...)

 Thank you!

See in std.traits: isSomeFunction isCallable ReturnType ParameterTypeTuple You can use those in template constraints/static assert conditions.
Jan 12 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday 12 January 2011 00:21:54 %u wrote:
 Hi,
 
 Is there any way to specify a parameter as "something that can be called
 with parameter types A, B, C and that returns a value of type D", without
 caring whether it's a delegate, a function, or an object that overloads
 opCall? (This might require the use of templates, but I still can't figure
 it out...)
 
 Thank you!

Take this example from my proposed std.unittests which is currently under review: void assertPred(alias pred, string msg = null, string file = __FILE__, size_t line = __LINE__, T...) (T args) if(isCallable!pred && is(ReturnType!pred == bool) && __traits(compiles, pred(args)) && isPrintable!T) { immutable result = pred(args); if(!result) { string argsStr; if(args.length > 0) { foreach(value; args) argsStr ~= format("[%s], ", to!string(value)); argsStr.popBackN(", ".length); } else argsStr = "none"; if(msg.empty) throw new AssertError(format("assertPred failed: arguments: %s.", argsStr), file, line); else throw new AssertError(format("assertPred failed: arguments: %s: %s", argsStr, msg), file, line); } } The combination of alias, isCallable, ReturnType, and __traits(compiles) does the trick. isCallable guarantees that pred is callable, but it doesn't care how. ReturnType guarantees that the return type is bool. And __traits(compiles, pred(args)) guarantees that pred can be called with the given arguments. - Jonathan M Davis
Jan 12 2011
prev sibling parent Lutger Blijdestijn <lutger.blijdestijn gmail.com> writes:
%u wrote:

 Hi,
 
 Is there any way to specify a parameter as "something that can be called
 with parameter types A, B, C and that returns a value of type D", without
 caring whether it's a delegate, a function, or an object that overloads
 opCall? (This might require the use of templates, but I still can't figure
 it out...)
 
 Thank you!

Yes, look at std.traits. isCallable determines if a type can be called, ReturnType gives you the return type and ParameterTypeTuple obtaines a tuple of the parameters. In this example I used all three, isCallable is implied by the latter two so that is actually redundant: import std.traits; import std.stdio; import std.typetuple; int square(int a) { return a*a; } struct Squarer { int opCall(int a) { return a * a; } } void foo(T)(T fun, int num) if (isCallable!(T) && is(ReturnType!fun == int) && is(ParameterTypeTuple!(T) == TypeTuple!(int))) { writeln("square of ", num, ":", fun(num)); } void main() { foo(&square, 2); Squarer functor; foo(functor, 2); foo((int a) { return a * a; }, 2); } Another (efficient) way to do this is with alias template parameters, this determines not the type of the object / function / delegate, but the actual symbol directly. However, it must be able to access that symbol, see this example: void foo2(alias fun)(int num) if (isCallable!(fun) && is(ReturnType!(fun) == int) && is(ParameterTypeTuple!(fun) == TypeTuple!(int))) { writeln("square of ", num, ":", fun(num)); } void main() { Squarer functor; foo2!square(2); //foo2!functor(2); error: cannot access frame of function D main foo2!((int a) { return a * a; })(2); } foo2 is trying to call opCall of the functor object, but it is a local variable of the main function so it cannot be called this way.
Jan 12 2011