鸭子在D打字

Dan*_*Dan 6 d duck-typing metaprogramming

我是D的新手,我想知道是否可以方便地进行编译时检查的鸭子打字.

例如,我想定义一组方法,并要求为传递给函数的类型定义这些方法.它与interfaceD 略有不同,因为我不必在任何地方声明"类型X实现接口Y" - 只能找到方法,否则编译将失败.此外,允许在任何类型上发生这种情况都是好的,而不仅仅是结构和类.我能找到的唯一资源是这个电子邮件线程,它表明以下方法是一种不错的方法:

void process(T)(T s)
    if( __traits(hasMember, T, "shittyNameThatProbablyGetsRefactored"))
    // and presumably something to check the signature of that method
{
    writeln("normal processing");
}
Run Code Online (Sandbox Code Playgroud)

...并建议你可以将它变成一个库调用Implements,以便以下可能:

struct Interface {
    bool foo(int, float);
    static void boo(float);
    ...
}

static assert (Implements!(S, Interface));
struct S {
    bool foo(int i, float f) { ... }
    static void boo(float f) { ... }
    ...
}

void process(T)(T s) if (Implements!(T, Interface)) { ... }
Run Code Online (Sandbox Code Playgroud)

对于未在类或结构中定义的函数,是否可以执行此操作?还有其他/新方法吗?有类似的事吗?

显然,这组约束类似于Go的类型系统.我不是要开始任何火焰战争 - 我只是以一种Go也会很好用的方式使用D.

Jon*_*vis 7

这实际上是D中常见的事情.这是范围如何工作.例如,最基本的范围类型 - 输入范围 - 必须具有3个功能:

bool empty();  //Whether the range is empty
T front();  // Get the first element in the range
void popFront();  //pop the first element off of the range
Run Code Online (Sandbox Code Playgroud)

然后使用模板化函数std.range.isInputRange来检查类型是否是有效范围.例如,最基本的重载std.algorithm.find看起来像

R find(alias pred = "a == b", R, E)(R haystack, E needle)
if (isInputRange!R &&
    is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
{ ... }
Run Code Online (Sandbox Code Playgroud)

isInputRange!Rtrue如果R是一个有效的输入范围内,并且is(typeof(binaryFun!pred(haystack.front, needle)) : bool)true如果pred接受haystack.frontneedle并返回一个类型的隐式转换为bool.所以,这个重载完全基于静态鸭子类型.

至于isInputRange它本身,它看起来像

template isInputRange(R)
{
    enum bool isInputRange = is(typeof(
    {
        R r = void;       // can define a range object
        if (r.empty) {}   // can test for empty
        r.popFront();     // can invoke popFront()
        auto h = r.front; // can get the front of the range
    }));
}
Run Code Online (Sandbox Code Playgroud)

它是一个同名模板,所以当它被使用时,它会被带有名称的符号替换,在这种情况下,它是一个类型的枚举bool.这bool就是true表达式的类型是非void.typeof(x)导致void表达式无效; 否则,它是表达式的类型x.而is(y)结果true,如果y是非void.因此,如果表达式中的代码编译,isInputRange将最终成为,否则.truetypeoffalse

表达式isInputRange验证您可以声明一个类型的变量R,该变量R具有一个empty可以在条件中使用的成员(可以是函数,变量或其他),该R函数名为popFront不带参数的函数,并且R具有front返回值的成员.这是输入范围所期望的API,并且typeof如果R遵循该API,则内部表达式将编译,因此isInputRangetrue用于该类型.否则,它会false.

D的标准库有很多这样的同名模板(通常称为traits),并在模板约束中大量使用它们.std.traits特别是有很多.所以,如果你想要更多关于如何编写这些特征的例子,你可以在那里查看(虽然其中一些相当复杂).这些特性的内部结构并不总是特别漂亮,但它们确实很好地封装了鸭子类型测试,因此模板约束更清晰,更易理解(如果将这些测试直接插入到它们中,它们会更加,更加丑陋).

所以,这是D中静态鸭子打字的常规方法.确实需要一些练习来弄清楚如何编写它们,但这是标准的方法,它可以工作.曾经有人建议尝试提出类似于你的Implements!(S, Interface)建议的东西,但实际上还没有真正的东西,而这种方法实际上不那么灵活,使它不适合很多特征(虽然它当然可以与基本的一起工作).无论如何,我在这里描述的方法目前是标准的方法.

另外,如果您对范围了解不多,我建议您阅读此内容.