Обмен делегатом / функцией и интерфейсом в D2 - PullRequest
6 голосов
/ 08 сентября 2010

Я хотел бы иметь возможность определить функцию, которая принимает интерфейс, но может быть выполнена с делегатом или функцией, которая обеспечивает ту же функциональность.Например, в C ++ я могу написать что-то вроде:

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

void fun(toInt dg) { ... }

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

int impl2(float x) { ... }

, а затем вызвать его, используя любую реализацию:

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

(Это преобразование с плавающей точкой -> int является лишь упрощенным примером дляпроиллюстрируйте принцип, а не мою реальную функциональность).

Я бы хотел добиться чего-то похожего в D. Моя наивная попытка была такой:

interface toInt {
    int opCall(float);
}

void fun(toInt dg) { ... }

int impl2(float x) { ... }

fun(impl2);

Компилятор жалуется на этой последней строкечто он не может неявно преобразовать impl2 в тип toInt.Возможно, я могу просто добавить перегруженную реализацию fun и сделать преобразование явным, но мне было интересно, есть ли более элегантный и общий способ сделать это, как в примере C ++ выше.

Ответы [ 3 ]

5 голосов
/ 08 сентября 2010

he_the_great в принципе правильно, но следующее небольшое изменение заставит его работать и со структурами / классами с поддержкой opCall.

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 голосов
/ 08 сентября 2010

Как упоминает he_the_great, шаблоны являются лучшим решением в большинстве случаев. (Это так или иначе делает std :: function.) Если вы не можете / не хотите использовать шаблоны, посмотрите в std.functional. Вы найдете функцию с именем toDelegate(), которая использует некоторую хитрую магию, чтобы превратить указатель на функцию в делегат. Затем вы можете сделать это:

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

Вы также можете написать что-то эквивалентное std::function в C ++, что, вероятно, будет тривиальным. На самом деле, я сделаю это прямо здесь. Обратите внимание, что это неправильно обрабатывает параметры ref или out или возвращает прямо сейчас из-за ошибок компилятора.

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 голосов
/ 08 сентября 2010

D различает функции и делегаты, поскольку делегаты больше, чем просто указатель на функцию.Так что нет типа, который будет работать для обоих.Обсуждалось добавление оболочки для функций в Фобос (как указывает dsimcha, это std.functional.toDelegate).Вы можете использовать шаблоны (у меня нет доступа к последнему компилятору, чтобы проверить, работает ли он)

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