Co to jest "callback" w C i jak są one realizowane?

Z lektury, którą zrobiłem, Core Audio opiera się w dużej mierze na wywołaniach zwrotnych (i C++, ale to już inna historia).

Rozumiem pojęcie (rodzaj) tworzenia funkcji, która jest wywoływana przez inną funkcję wielokrotnie do wykonania zadania. Po prostu nie rozumiem, jak się je ustawia i jak naprawdę działają. Wszelkie przykłady będą mile widziane.

 132
Author: Sourav Ghosh, 2008-09-27

8 answers

Nie ma "callback" w C-nie bardziej niż jakakolwiek inna ogólna koncepcja programowania.

Są zaimplementowane za pomocą wskaźników funkcji. Oto przykład:

void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
    for (size_t i=0; i<arraySize; i++)
        array[i] = getNextValue();
}

int getNextRandomValue(void)
{
    return rand();
}

int main(void)
{
    int myarray[10];
    populate_array(myarray, 10, getNextRandomValue);
    ...
}

Tutaj funkcja populate_array przyjmuje wskaźnik funkcji jako trzeci parametr i wywołuje go, aby uzyskać wartości do wypełnienia tablicy. Napisaliśmy wywołanie zwrotne getNextRandomValue, które zwraca losową wartość i przekazaliśmy do niego wskaźnik populate_array. populate_array wywoła naszą funkcję callback 10 razy i przypisze zwrócone wartości do elementów w podanej tablicy.

 175
Author: aib,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-11 03:00:23

Oto przykład wywołania zwrotnego w C.

Załóżmy, że chcesz napisać kod, który pozwala na wywołanie wywołań zwrotnych, gdy wystąpi jakieś zdarzenie.

Najpierw określ typ funkcji użytej do wywołania zwrotnego:

typedef void (*event_cb_t)(const struct event *evt, void *userdata);

Teraz zdefiniuj funkcję używaną do rejestracji wywołania zwrotnego:

int event_cb_register(event_cb_t cb, void *userdata);

Tak wyglądałby kod rejestrujący wywołanie zwrotne:

static void my_event_cb(const struct event *evt, void *data)
{
    /* do stuff and things with the event */
}

...
   event_cb_register(my_event_cb, &my_custom_data);
...

Wewnątrz dyspozytora zdarzeń wywołanie zwrotne może być przechowywane w strukturze, która wygląda mniej więcej tak:

struct event_cb {
    event_cb_t cb;
    void *data;
};

Tak wygląda kod, który wykonuje wywołanie zwrotne.

struct event_cb *callback;

...

/* Get the event_cb that you want to execute */

callback->cb(event, callback->data);
 114
Author: Russell Bryant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-29 01:47:54

Prosty program oddzwaniający. Mam nadzieję, że odpowie na twoje pytanie.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include "../../common_typedef.h"

typedef void (*call_back) (S32, S32);

void test_call_back(S32 a, S32 b)
{
    printf("In call back function, a:%d \t b:%d \n", a, b);
}

void call_callback_func(call_back back)
{
    S32 a = 5;
    S32 b = 7;

    back(a, b);
}

S32 main(S32 argc, S8 *argv[])
{
    S32 ret = SUCCESS;

    call_back back;

    back = test_call_back;

    call_callback_func(back);

    return ret;
}
 18
Author: Gautham Kantharaju,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-14 12:37:30

Funkcja zwrotna w C jest odpowiednikiem parametru / zmiennej funkcji przypisanej do użycia w innej funkcji. Przykład Wiki

W poniższym kodzie

#include <stdio.h>
#include <stdlib.h>

/* The calling function takes a single callback as a parameter. */
void PrintTwoNumbers(int (*numberSource)(void)) {
    printf("%d and %d\n", numberSource(), numberSource());
}

/* A possible callback */
int overNineThousand(void) {
    return (rand() % 1000) + 9001;
}

/* Another possible callback. */
int meaningOfLife(void) {
    return 42;
}

/* Here we call PrintTwoNumbers() with three different callbacks. */
int main(void) {
    PrintTwoNumbers(&rand);
    PrintTwoNumbers(&overNineThousand);
    PrintTwoNumbers(&meaningOfLife);
    return 0;
}

Funkcja (*numberSource) wewnątrz wywołania funkcji PrintTwoNumbers jest funkcją do "call back" / wykonania z wnętrza PrintTwoNumbers, zgodnie z kodem, który działa.

Więc jeśli masz coś takiego jak funkcja pthread, możesz przypisać inną funkcję do uruchomienia wewnątrz pętli z jego instancjacja.

 8
Author: daemondave,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-07 01:08:14

Wywołania zwrotne w C są zwykle implementowane za pomocą wskaźników funkcji i powiązanych wskaźników danych. Przekazujesz swoją funkcję on_event() i wskaźniki danych do funkcji frameworka watch_events() (na przykład). Gdy ma miejsce zdarzenie, funkcja jest wywoływana z danymi i niektórymi danymi specyficznymi dla zdarzenia.

Wywołania zwrotne są również używane w programowaniu GUI. GTK + tutorial ma ładną sekcję dotyczącą teorii sygnałów i wywołań zwrotnych .

 4
Author: John Millikin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-27 02:36:48

Ten artykuł Wikipedii mA przykład w C.

Dobrym przykładem jest to, że nowe moduły napisane w celu zwiększenia rejestru serwera Apache Web z głównym procesem apache, przekazując im Wskaźniki funkcji, aby te funkcje były wywoływane z powrotem do przetwarzania żądań stron internetowych.

 3
Author: Leonard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-27 02:41:03

Zazwyczaj można to zrobić za pomocą wskaźnika funkcji, który jest specjalną zmienną, która wskazuje na lokalizację pamięci funkcji. Następnie można użyć tego do wywołania funkcji z określonymi argumentami. Więc prawdopodobnie będzie funkcja, która ustawia funkcję zwrotną. To zaakceptuje wskaźnik funkcji, a następnie zapisze ten adres gdzieś, gdzie może być użyty. Następnie, gdy określone zdarzenie zostanie wywołane, wywoła ona tę funkcję.

 0
Author: mdec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-27 02:37:01

Callback w C jest funkcją, która jest dostarczana do innej funkcji, aby "oddzwonić" w pewnym momencie, gdy inna funkcja wykonuje swoje zadanie.

Istnieją dwa sposoby użycia wywołania zwrotnego : synchroniczne wywołanie zwrotne i asynchroniczne wywołanie zwrotne. Synchroniczne wywołanie zwrotne jest do innej funkcji, która ma zamiar zrobić pewne zadanie, a następnie powrócić do dzwoniącego z zadania zakończone. Asynchroniczne wywołanie zwrotne jest dostarczane do innej funkcji, która ma rozpocząć zadanie a następnie wróć do rozmówcy z zadaniem, które prawdopodobnie nie zostało zakończone.

Synchroniczne wywołanie zwrotne jest zwykle używane do zapewnienia delegata do innej funkcji, do której inna funkcja deleguje jakiś krok zadania. Klasycznymi przykładami tej delegacji są funkcje bsearch() i qsort() ze standardowej biblioteki C. Obie te funkcje przyjmują wywołanie zwrotne, które jest używane podczas zadania, które dostarcza funkcja, aby Typ przeszukiwanych danych, w przypadku bsearch(), lub sortowane, w przypadku `qsort (), nie musi być znane przez używaną funkcję.

Na przykład oto mały przykładowy program z bsearch() wykorzystujący różne funkcje porównawcze, synchroniczne wywołania zwrotne. Pozwalając nam delegować porównanie danych do funkcji zwrotnej, funkcja bsearch() pozwala nam zdecydować w czasie wykonywania, jakiego rodzaju porównania chcemy użyć. Jest to synchroniczne, ponieważ gdy funkcja bsearch() zwróci zadanie jest zakończone.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    int iValue;
    int kValue;
    char label[6];
} MyData;

int cmpMyData_iValue (MyData *item1, MyData *item2)
{
    if (item1->iValue < item2->iValue) return -1;
    if (item1->iValue > item2->iValue) return 1;
    return 0;
}

int cmpMyData_kValue (MyData *item1, MyData *item2)
{
    if (item1->kValue < item2->kValue) return -1;
    if (item1->kValue > item2->kValue) return 1;
    return 0;
}

int cmpMyData_label (MyData *item1, MyData *item2)
{
    return strcmp (item1->label, item2->label);
}

void bsearch_results (MyData *srch, MyData *found)
{
        if (found) {
            printf ("found - iValue = %d, kValue = %d, label = %s\n", found->iValue, found->kValue, found->label);
        } else {
            printf ("item not found, iValue = %d, kValue = %d, label = %s\n", srch->iValue, srch->kValue, srch->label);
        }
}

int main ()
{
    MyData dataList[256] = {0};

    {
        int i;
        for (i = 0; i < 20; i++) {
            dataList[i].iValue = i + 100;
            dataList[i].kValue = i + 1000;
            sprintf (dataList[i].label, "%2.2d", i + 10);
        }
    }

//  ... some code then we do a search
    {
        MyData srchItem = { 105, 1018, "13"};
        MyData *foundItem = bsearch (&srchItem, dataList, 20, sizeof(MyData), cmpMyData_iValue );

        bsearch_results (&srchItem, foundItem);

        foundItem = bsearch (&srchItem, dataList, 20, sizeof(MyData), cmpMyData_kValue );
        bsearch_results (&srchItem, foundItem);

        foundItem = bsearch (&srchItem, dataList, 20, sizeof(MyData), cmpMyData_label );
        bsearch_results (&srchItem, foundItem);
    }
}

Asynchroniczne wywołanie zwrotne jest różni się tym, że gdy wywołana funkcja, do której dostarczamy callback powraca, zadanie może nie zostać zakończone. Ten typ wywołania zwrotnego jest często używany z asynchronicznym We/Wy, w którym operacja We/Wy jest uruchamiana, a następnie, gdy jest zakończona, wywołanie zwrotne jest wywoływane.

W poniższym programie tworzymy gniazdo do nasłuchiwania żądań połączenia TCP i gdy żądanie zostanie odebrane, funkcja wykonująca listen wywołuje podaną funkcję callback. Ta prosta aplikacja może być uruchamiając go w jednym oknie, korzystając z narzędzia telnet lub przeglądarki internetowej, aby spróbować połączyć się w innym oknie.

Usunąłem większość kodu WinSock z przykładu, który Microsoft dostarcza z accept() funkcji w https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526 (v=vs.85). aspx

Ta aplikacja uruchamia listen() na lokalnym hoście, 127.0.0.1, używając portu 8282, więc możesz użyć telnet 127.0.0.1 8282 lub http://127.0.0.1:8282/.

Ta przykładowa aplikacja został stworzony jako aplikacja konsolowa z Visual Studio 2017 Community Edition i korzysta z wersji Microsoft WinSock sockets. W przypadku aplikacji Linuksowych funkcje WinSock musiałyby zostać zastąpione alternatywami dla Linuksa, a Biblioteka wątków Windows użyłaby pthreads.

#include <stdio.h>
#include <winsock2.h>
#include <stdlib.h>
#include <string.h>

#include <Windows.h>

// Need to link with Ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

// function for the thread we are going to start up with _beginthreadex().
// this function/thread will create a listen server waiting for a TCP
// connection request to come into the designated port.
// _stdcall modifier required by _beginthreadex().
int _stdcall ioThread(void (*pOutput)())
{
    //----------------------
    // Initialize Winsock.
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        printf("WSAStartup failed with error: %ld\n", iResult);
        return 1;
    }
    //----------------------
    // Create a SOCKET for listening for
    // incoming connection requests.
    SOCKET ListenSocket;
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {
        wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port for the socket that is being bound.
    struct sockaddr_in service;
    service.sin_family = AF_INET;
    service.sin_addr.s_addr = inet_addr("127.0.0.1");
    service.sin_port = htons(8282);

    if (bind(ListenSocket, (SOCKADDR *)& service, sizeof(service)) == SOCKET_ERROR) {
        printf("bind failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    //----------------------
    // Listen for incoming connection requests.
    // on the created socket
    if (listen(ListenSocket, 1) == SOCKET_ERROR) {
        printf("listen failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    //----------------------
    // Create a SOCKET for accepting incoming requests.
    SOCKET AcceptSocket;
    printf("Waiting for client to connect...\n");

    //----------------------
    // Accept the connection.
    AcceptSocket = accept(ListenSocket, NULL, NULL);
    if (AcceptSocket == INVALID_SOCKET) {
        printf("accept failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    else
        pOutput ();   // we have a connection request so do the callback

    // No longer need server socket
    closesocket(ListenSocket);

    WSACleanup();
    return 0;
}

// our callback which is invoked whenever a connection is made.
void printOut(void)
{
    printf("connection received.\n");
}

#include <process.h>

int main()
{
     // start up our listen server and provide a callback
    _beginthreadex(NULL, 0, ioThread, printOut, 0, NULL);
    // do other things while waiting for a connection. In this case
    // just sleep for a while.
    Sleep(30000);
}
 0
Author: Richard Chambers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-02 16:03:37