D:代表还是回电?

时间:2015-10-21 06:55:45

标签: callback delegates d

我发现代表的概念对我来说很难。我真的不明白为什么我不能简单地将一个函数传递给另一个函数并且需要将它包装到Delegate中。我在文档中读到,有些情况下我不知道它的名字,而委托只是打电话的方式。

但是现在我无法理解回调的概念。我试图找到更多信息,但我不能理解它是否只是调用其他函数或者它是什么。

您能否展示D回调的示例并说明它们可以提供哪些帮助?

import vibe.d;

shared static this()
{
    auto settings = new HTTPServerSettings;
    settings.port = 8080;

    listenHTTP(settings, &handleRequest);
}

void handleRequest(HTTPServerRequest req,
                   HTTPServerResponse res)
{
    if (req.path == "/")
        res.writeBody("Hello, World!", "text/plain");
}

&handleRequest是回调吗?它是如何工作的以及它在什么时候开始的?

4 个答案:

答案 0 :(得分:3)

因此在内存中,函数只是一堆字节。像数组一样,你可以指向它。这是一个函数指针。它的类型为RETT function(ARGST)。其中RETT是返回类型,ARGST是参数类型。当然,属性可以像任何函数声明一样应用。

现在,委托是一个函数指针,带有上下文指针。上下文指针可以是单个整数(参数),调用帧(另一个内部的函数)或最后一个类/结构的任何内容。

委托与RETT delegate(ARGST)处的函数指针类型非常相似。它们不可互换,但您可以很容易地将函数指针转换为委托指针。

回调的概念就是说,嘿,我知道你会知道X所以当发生这种情况时,请通过调用这个函数/委托告诉我关于X的事。

要回答关于&handleRequest的问题,是的,这是一个回调。

答案 1 :(得分:2)

OP中有几个问题。我将尝试回答以下两个问题:

问:您能举例说明D回调并说明它们可以提供哪些帮助吗?

答:它们通常用于支持代理的所有语言(例如C#)作为事件处理程序。 - 只要触发事件,就可以调用委托。不支持委托的语言为此目的使用类或回调函数。示例如何使用FLTK 2.0 libraryhttp://www.fltk.org/doc-2.0/html/group__example2.html在C ++中使用回调。代表们非常适合这一点,因为他们可以直接访问上下文。当您为此目的使用回调时,您必须传递要在回调中修改的所有对象...检查提到的FLTK链接作为示例 - 我们必须将指针传递给fltk::Window对象window_callback函数以便操作它。 (之所以FLTK这样做是因为FLTK背后诞生了C ++没有lambdas,否则他们会使用它们而不是回调)

示例D使用:http://dlang.org/phobos/std_signals.html

问:为什么我不能简单地将一个函数传递给另一个函数并需要将其包装到Delegate中?

答:你不必包装给代表 - 这取决于你想要完成的事情......有时传递回调只会对你有用。您无法访问可能要调用回调的上下文,但代理可以。但是,您可以传递上下文(这就是某些C / C ++库所做的事情)。

我认为您在D language reference

中解释了您的要求

引用1:

  

函数指针可以指向静态嵌套函数

引用2:

  

委托可以设置为非静态嵌套函数

查看该部分的最后一个示例,并注意委托如何成为方法:

struct Foo
{
    int a = 7;
    int bar() { return a; }
}

int foo(int delegate() dg)
{
    return dg() + 1;
}

void test()
{
    int x = 27;
    int abc() { return x; }
    Foo f;
    int i;

    i = foo(&abc);   // i is set to 28
    i = foo(&f.bar); // i is set to 8
}

答案 2 :(得分:2)

您可以将函数传递给其他函数以便以后调用。

void test(){}
void receiver(void function() fn){
    // call it like a normal function with 'fn()'
    // or pass it around, save it, or ignore it
}

// main
receiver(&test); // 'test' will be available as 'fn' in 'receiver'

您需要在函数名前加上&作为参数,以阐明您想要传递函数指针。如果你不这样做,它将调用该函数,因为UFCS(没有大括号调用)。它还不是代表。

接收你的callable的函数可以用它做任何想做的事。一个常见的例子是您的问题,一个Web服务回调。首先,您告诉框架在收到请求时应该做什么(通过在函数中定义操作并使该函数可用于框架),并在您的示例中输入一个listenHTTP的循环,该循环在您调用代码时调用它收到一个请求。如果您想进一步了解此主题:https://en.wikipedia.org/wiki/Event_(computing)#Event_handler

委托是附加了上下文信息的函数指针。假设您要添加处理当前上下文中可用的其他元素的处理程序。就像一个将指示器变为红色的按钮。例如:

class BuildGui {

    Indicator indicator;
    Button button;

    this(){
        ... init
        button.clickHandler({ // curly braces: implicit delegate in this case
            indicator.color = "red"; // notice access of BuildGui member
        });
        button.clickHandler(&otherClickHandler); // methods of instances can be delegates too
    }

    void otherClickHandler(){
        writeln("other click handler");
    }

}

在这个虚构的Button类中,所有点击处理程序都会保存到列表中,并在单击时调用。

答案 3 :(得分:0)

已经有了很好的答案。我只是想尝试做一个简单的总结。

简单地说:委托允许您使用方法作为回调。

在C中,您通过显式传递对象(多次命名上下文)为void *并将其转换为(希望)正确的类型来执行相同的操作:

    <a href="https://api.instagram.com/oauth/authorize/?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=code">Get Instagram images</a>

在C ++中,当你想使用方法作为回调时,你也会这样做。你创建一个函数将对象指针显式地作为void *,将其强制转换为(希望)正确的类型,并调用方法:

void callback(void *context, ...) {
    /* Do operations with context, which is usually a struct */

    doSomething((struct DATA*)context, ...);
    doSomethingElse((struct DATA*)context, ...);
}

委托使这一切都是隐含的。