2010-09-07 20 views
6

Me gustaría poder definir una función que toma una interfaz, pero se puede cumplir con un delegado o función que brinde la misma funcionalidad. Por ejemplo, en C++ que puedo escribir algo como:Intercambiando delegado/función e interfaz en D2

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

void fun(toInt dg) { ... } 

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

int impl2(float x) { ... } 

Y luego llamo utilizando la aplicación:

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

(Este Float-> conversión int es sólo un ejemplo simplificado para ilustrar el principio, no es mi funcionalidad real).

me gustaría lograr algo similar en D. mi ingenuo intento fue así:

interface toInt { 
    int opCall(float); 
} 

void fun(toInt dg) { ... } 

int impl2(float x) { ... } 

fun(impl2); 

El compilador se queja en esta última línea que no se puede convertir implícitamente el tipo impl2 Toint. Probablemente pueda agregar una implementación de diversión sobrecargada y hacer que la conversión sea explícita, pero me preguntaba si habría una manera más elegante y general de hacerlo, como en el ejemplo anterior de C++.

Respuesta

5

he_the_great lo tiene, básicamente, a la derecha, pero el siguiente pequeño cambio hará que funcione con estructuras/clases opCall habilitados también.

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

Gracias por ayudar a un novato :) Esta parece ser la solución más práctica. Sin embargo, agrega una capa adicional de direccionamiento indirecto desde la perspectiva de la persona que llama: está llamando a fun (x, f) en lugar de a f (x), como sería el caso con std :: function. Traté de arreglar esto con std.curry! (invirtiendo el orden de los parámetros en fun() y usando alias curry! (fun, x) fun1; fun1 (0.0);), pero el compilador no está contento: Error: "template tst.main.curry! (fun, x) .curry (T) if (is (typeof (fun (arg, T.init)))) no coincide con ninguna declaración de plantilla de función "(no se puede deducir el argumento de la plantilla). – Eitan

+0

Pude hacerlo funcionar usando [alias] curry! (Fun! Impl1, x) funX; funX (4.4); [/ code] El problema aquí es que la plantilla 'diversión' debe expandirse a la función 'diversión' antes de que 'curry' sepa cómo trabajar con ella. Este es probablemente un escenario bastante común que debería agregarse al stdlib, sin embargo. Por supuesto, podrías hacerlo más genérico usando el cursivo alias [code] (¡diversión! (Typeof (x)), x) funX; [/ code] para que no tenga que repetir todo el tipo de x, posiblemente complejo, para curry de consumidor. –

3

D hace una distinción de funciones y delegados ya que los delegados son más grandes que solo un puntero a una función. Entonces no hay ningún tipo que funcione para ambos. Se ha debatido la posibilidad de agregar un contenedor para funciones en Phobos (como señala dsimcha, es estándar.funcional.paraDelegar). Puede hacer uso de plantillas (no tengo acceso a la última compilador para comprobar si esto funciona)

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

Según lo mencionado por he_the_great, las plantillas son la mejor solución en la mayoría de los casos. (Eso es waht std :: function lo está haciendo de todos modos.) Si no puede/no quiere usar plantillas, busque en std.functional. Encontrará una función llamada toDelegate(), que utiliza una magia furtiva para convertir un puntero de función en un delegado. A continuación, puede hacer esto:

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

También podría escribir algo equivalente a C++ 's std::function, lo que probablemente sería trivial. De hecho, lo haré aquí mismo. Sin embargo, tenga en cuenta que esto no controla correctamente los parámetros ref o out o los devuelve en este momento debido a errores del compilador.

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