2015-10-21 1 views
4

Мне очень понравилась концепция делегатов. Я действительно не понимаю, почему я не могу просто передать одну функцию другому, и мне нужно передать ее делегату. Я читал в документах, что есть некоторые случаи, когда я не знаю, что это имя, и делегат - это только способ его называть.D: Делегаты или обратные вызовы?

Но теперь у меня возникают проблемы с пониманием концепции обратных вызовов. Я пытался найти больше информации, но я не могу понять, это просто вызов другой функции или что это такое.

Не могли бы вы показать примеры обратных вызовов 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 это обратный вызов? Как это работает и в какой момент это начинается?

ответ

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 (вызов без привязок). Это еще не делегат.

Функция, которая получает ваш вызываемый, может делать с ней все, что захочет. Общим примером является ваш вопрос, обратный вызов веб-службы. Сначала вы сообщаете структуре, что должно быть сделано в случае получения запроса (путем определения действий в функции и предоставления этой функции для фреймворка), а в вашем примере введите цикл с 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

Таким образом, в памяти функция представляет собой всего лишь кучу байтов. Как массив, вы можете взять указатель на него. Это указатель на функцию. Он имеет тип RETT function(ARGST) в D. Где RETT - тип возвращаемого значения, а ARGST - типы аргументов. Конечно, атрибуты могут применяться как любое объявление функции.

Теперь делегаты являются указателем на функцию с указателем на контекст. Указатель контекста может быть любым из одного целого (аргумент), кадра вызова (функция внутри другого) или, наконец, класса/структуры.

Делегат очень похож на тип указателя функции на RETT delegate(ARGST). Они не взаимозаменяемы, но вы можете легко превратить указатель на указатель делегата.

Концепция обратного вызова состоит в том, чтобы сказать, эй, я знаю, что вы будете знать о X, поэтому, когда это произойдет, расскажите мне про X, вызвав эту функцию/делегат.

Чтобы ответить на ваш вопрос о &handleRequest, да, это обратный вызов.

+0

Я бы также добавил: Обычно, когда функция принимает обратный вызов как параметр, тип обратного вызова является 'делегатом', так как он может принимать как обычные функции, так и делегаты. Тривиально преобразовать 'функцию' в' делегат' (как сказано здесь), но преобразование 'делегата' в' функцию' потребует генерации нового кода функции во время выполнения (что вполне осуществимо, но не такого рода хак вы обычно хотели бы иметь дело). – Cauterite

+0

Возможно, вы захотите упомянуть ['toDelegate'] (http://dlang.org/phobos/std_functional.html#toDelegate), чтобы уточнить« вы можете легко превратить указатель на указатель делегата ». Они легко, но не _implicitly_ конвертируемые. – rcorre

2

В OP было несколько вопросов. Я попытаюсь ответить на следующие два вопроса:

Вопрос: Не могли бы вы показать примеры обратных вызовов D и объяснить, где они могут быть полезны?

A: Они обычно используются на всех языках, поддерживающих делегаты (C# для примера) в качестве обработчиков событий. - Вы даете делегату называться всякий раз, когда происходит событие. Языки, которые не поддерживают делегатов, используют для этого классы либо функции обратного вызова. Пример использования обратных вызовов в C++ с использованием FLTK 2.0 library: http://www.fltk.org/doc-2.0/html/group__example2.html. Делегаты идеально подходят для этого, поскольку они могут напрямую обращаться к контексту. Когда вы используете обратные вызовы для этой цели, вам необходимо передать все объекты, которые вы хотите изменить в обратном вызове ... Проверьте упомянутую ссылку FLTK в качестве примера - нам нужно передать указатель на объект fltk::Window на функцию window_callback в чтобы управлять им. (Причина, почему FLTK делает это в том, что обратно FLTK родился C++ не имеют лямбды, в противном случае они будут использовать их вместо обратных вызовов)

Пример D использования: http://dlang.org/phobos/std_signals.html

Q: Почему я не могу просто передать одну функцию другому и передать ее делегату?

A: Вам не нужно обернуть делегатов - это зависит от того, что вы хотите достичь ... Иногда прохождение обратных вызовов будет просто работать для вас. Вы не можете получить доступ к контексту, в котором вы можете вызвать обратный вызов, но делегаты могут. Однако вы можете передать контекст (и это то, что делают некоторые библиотеки 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 
} 
0

Есть уже отличные ответы. Я просто хочу попытаться сделать простое резюме.

Просто: делегат позволяет использовать методы в качестве обратных вызовов.

В C, вы делаете то же самое, явно передавая объект (много раз назвал контекст) недействительным * и приведение его (надеюсь) правильный тип:

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

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

В C++, вы делаете то же самое, когда желая использовать метод в качестве обратного вызова.Вы делаете функцию, принимающую указатель на объект в явном виде ничтожной *, бросайте его (надеюсь) правильный тип, и вызов метода:

void callback(void* object, ...) { 
    ((MyObject*)object)->method(...); 
} 

Делегат делает все это неявно.