我希望能够定义一个接受接口的函数,但可以使用提供相同功能的委托或函数来实现。例如,在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);
(这个float-&gt; int转换只是一个简单的例子来说明原理,而不是我的实际功能。)
我想在D中实现类似的东西。我天真的尝试是这样的:
interface toInt {
int opCall(float);
}
void fun(toInt dg) { ... }
int impl2(float x) { ... }
fun(impl2);
编译器在最后一行抱怨它不能隐式地将impl2转换为toInt类型。我可能只是添加一个重载的实现的乐趣并使转换显式,但我想知道是否有更优雅和一般的方式这样做,如上面的C ++示例。
答案 0 :(得分: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 :(得分:3)
D区分函数和委托,因为委托大于指向函数的指针。所以没有类型可以兼顾两者。已经讨论过将函数的包装器添加到Phobos中(如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);
}
答案 2 :(得分: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));
}
你也可以写一些与C ++ std::function
相当的东西,这可能是微不足道的。事实上,我会在这里做。请注意,虽然这不能正确处理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);
}