2010-09-07 55 views
6

我希望能够定义一个接受接口的函数,但可以使用提供相同功能的委托或函数来实现。例如,在C++我可以写类似:在D2中交换委托/功能和接口

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类型。我可能只是添加一个有趣的重载实现,并明确地进行转换,但我想知道是否有一个更优雅和通用的方法来做到这一点,就像上面的C++示例一样。

回答

5

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

感谢您帮助新手:)这似乎是最实际的解决方案。然而,它确实增加了从调用者的角度来看的额外间接层:你调用fun(x,f)而不是f(x),就像std :: function一样。我试图用std.curry解决这个问题! (反转参数顺序为fun()并使用别名curry!(fun,x)fun1; fun1(0.0);),但编译器不高兴:Error:“template tst.main.curry!(fun,x) .curry(T)if(is(typeof(fun(arg,T.init))))不匹配任何函数模板声明“(无法推导出模板参数)。 – Eitan 2010-09-09 00:57:49

+0

我能够通过使用[代码]别名咖喱!(fun!impl1,x)funX; funX(4.4); [/ code]这里的问题是,在'咖喱'知道如何使用它之前,模板'有趣'需要扩展到'有趣'功能。不过,这可能是一种常见的情况,应该将其添加到stdlib中。当然,你可以通过使用[code] alias curry!(fun!(typeof(x)),x)funX来使它更通用。 [/ code],以便您不必重复整个可能复杂的x类型,以便消费它的咖啡。 – 2010-09-09 03:36:02

3

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

正如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++,这将可能是微不足道的。事实上,我会在这里做。请注意,尽管由于编译器错误,它无法正确处理refout参数或返回。

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