views:

95

answers:

3

I'd like to be able to define a function that takes an interface, but can be fulfilled with a delegate or function that provide the same functionality. For example, in C++ I can write something like:

typedef std::function<int (float)> toInt;

void fun(toInt dg) { ... }

struct impl1 {
    int operator()(float x) { ... }
};

int impl2(float x) { ... }

And then call it using either implementation:

fun(impl1());
fun(&impl2);

(This float->int conversion is just a simplified example to illustrate the principle, not my actual functionality).

I'd like to achieve something similar in D. My naive attempt went like this:

interface toInt {
    int opCall(float);
}

void fun(toInt dg) { ... }

int impl2(float x) { ... }

fun(impl2);

The compiler complains on this last line that it cannot implicitly convert impl2 to toInt type. I can probably just add an overloaded implementation of fun and make the conversion explicit, but I was wondering if there was a more elegant and general way of doing this, as in the C++ example above.

+3  A: 

D makes a distinction of functions and delegates since delegates are larger than just a pointer to a function. So there is no type that will work for both. There has been discussion of adding a wrapper for functions into Phobos (As dsimcha points out, it is std.functional.toDelegate). You can make use of templates (I don't have access to the latest compiler to check if this works)

import std.traits;
import std.conv;

void fun(F)(F dg) if(isSomeFunction!(F) && __traits(compiles, int a = dg(35.6))
{  }

struct someStruct {
    int operator(float x) { return to!int(x); }
};

int impl2(float x) { return to!int(x); }


void main() {
    someStruct impl1;
    fun(&impl1.operator);
    fun(&impl2);
}
he_the_great
+3  A: 

As mentioned by he_the_great, templates are the best solution in most cases. (That's waht std::function is doing anyhow.) If you can't/don't want to use templates, look in std.functional. You'll find a function called toDelegate(), which uses some sneaky magic to turn a function pointer into a delegate. You can then do this:

import std.functional;

struct Impl1 {
    int doConversion(float x) { return cast(int) x; }
}

int impl2(float x) { return cast(int) x; }

void fun(int delegate(float) dg) {}

void main() {
    Impl1 impl1;
    fun(&impl1.doConversion);
    fun(toDelegate(&impl2));
}

You could also write something equivalent to C++'s std::function, which would likely be trivial. In fact, I'll do it right here. Note, though that this doesn't correctly handle ref or out parameters or returns right now due to compiler bugs.

import std.traits;

interface Function(ReturnType, Args...) {
    ReturnType opCall(Args);
}

class FunctionImpl(C) : Function!(ReturnType!C, ParameterTypeTuple!C)  {
    C callable;

    this(C callable) {
        this.callable = callable;
    }

    ReturnType!C opCall(ParameterTypeTuple!C args) {
        return callable(args);
    }
}

/**Convenience function for creating a Function object w/o explicitly 
  specifying types
 */
FunctionImpl!C functionObject(C)(C callable) {
     return new typeof(return)(callable);
}

// Test function.
uint inc(uint num) {
    return num + 1;
}

// Test it out.
void main() {
    auto myFun = functionObject(&inc);
    assert(myFun(1) == 2);
}
dsimcha
+5  A: 
Chris Nicholson-Sauls
Thanks for helping a newbie :) This seems like the most practical solution. It does add, however, and extra layer of indirection from the caller's perspective: you're calling fun(x, f) instead of just f(x), as would be the case with std::function. I tried to fix this with std.curry! (reversing parameter order to fun() and using alias curry!(fun, x) fun1; fun1(0.0);), but the compiler isn't happy: Error: "template tst.main.curry!(fun,x).curry(T) if (is(typeof(fun(arg,T.init)))) does not match any function template declaration" (can't deduce template argument).
Eitan
I was able to make it work by using [code] alias curry!( fun!impl1, x ) funX; funX( 4.4 ); [/code] The issue here is that the template 'fun' needs to be expanded into function 'fun' before 'curry' knows how to work with it. This is probably a common enough scenario that it should be added to the stdlib, though. Of course you could make it more generic by using [code] alias curry!( fun!( typeof( x ) ), x ) funX; [/code] so that you don't have to repeat the entire, possibly complex, type of x in order to curry it's consumer.
Chris Nicholson-Sauls