6
votes

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

3 Answers

5
votes

he_the_great has it basically right, but the following small change will make it work with opCall enabled structures/classes as well.

import std.conv;
import std.stdio;
import std.traits;

void fun ( T ) ( float value, T pred )
if ( __traits( compiles, pred( value ) ) && is( ReturnType!pred : int ) )
{
    writefln( "pred( %f ) -> %s", value, pred( value ) );
}

struct impl1 {
    int opCall ( float f ) {
        return to!int( f );
    }
}

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

void main () {
    impl1 x;
    fun( 1.0, x );

    fun( 2.0, &impl2 );

    fun( 3.0, ( float f ){ return to!int( f ); } );
}
3
votes

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);
}
3
votes

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);
}