O que é um “retorno de chamada” em C e como eles são implementados?

StackOverflow https://stackoverflow.com/questions/142789

  •  02-07-2019
  •  | 
  •  

Pergunta

A partir da leitura que eu fiz, Core Audio depende fortemente de chamadas de retorno (e C ++, mas isso é outra história).

Eu entendo o conceito (tipo de) de criação de uma função que é chamada por outra função repetidamente para realizar uma tarefa. Eu só não entendo como eles se configurar e como eles realmente funcionam. Quaisquer exemplos seria apreciada.

Foi útil?

Solução

Não há "retorno de chamada" em C -. Não mais do que qualquer outro conceito genérico de programação

Eles são implementados usando ponteiros de função. Aqui está um exemplo:

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);
    ...
}

Aqui, a função populate_array leva um ponteiro função como seu terceiro parâmetro, e chama-lo para obter os valores para preencher a matriz com. Nós escrevemos o getNextRandomValue callback, que retorna um valor aleatório-ish, e passou um ponteiro para ele para populate_array. populate_array vai chamar nossa função de retorno de 10 vezes e atribuir os valores devolvidos aos elementos na matriz dada.

Outras dicas

Aqui está um exemplo de retornos de chamada em C.

Vamos dizer que você quer escrever um código que permite registrar retornos de chamada para ser chamado quando algum evento ocorre.

Primeiro definir o tipo de função utilizado para a chamada de retorno:

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

Agora, definir uma função que é usada para registrar um callback:

int event_cb_register(event_cb_t cb, void *userdata);

Este é o código ficaria assim que registra um callback:

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);
...

Na parte interna do despachante evento, o retorno pode ser armazenado em uma struct que é algo como isto:

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

Isto é o que os olhares de código, como que executa uma chamada de retorno.

struct event_cb *callback;

...

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

callback->cb(event, callback->data);

Um programa de chamada de volta simples. Espero que responde a sua pergunta.

#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;
}

Uma chamada de retorno da função em C é o equivalente de um parâmetro da função / variável atribuído para ser utilizado dentro de outra função. Wiki Exemplo

No código abaixo,

#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;
}

A função (* numberSource) dentro das PrintTwoNumbers chamada de função é uma função de "call back" / executar a partir PrintTwoNumbers dentro como ditadas pelo código como ele é executado.

Então, se você tinha algo como uma função pthread você pode atribuir uma outra função para executar dentro do loop de sua instanciação.

As chamadas de retorno em C geralmente são implementados usando ponteiros de função e um ponteiro de dados associados. Você passar o seu on_event() função e ponteiros de dados a um watch_events() função de quadro (por exemplo). Quando um evento acontece, a sua função é chamada com os seus dados e alguns dados específicos do evento.

As chamadas de retorno também são usadas na programação GUI. A GTK + tutorial tem uma seção agradável na teoria de sinais e callbacks

Wikipedia artigo tem um exemplo em C.

Um bom exemplo é que novos módulos escrito para aumentar o registo do servidor Apache Web com o processo de apache principal, passando-os funcionar ponteiros para que essas funções são chamadas de volta para solicitações de página processo web.

A chamada de retorno em C é uma função que é fornecida para outra função para "chamada de volta para" em algum ponto quando a outra função está fazendo a sua tarefa.

duas maneiras que uma chamada de retorno é usados: retorno de chamada síncrona e assíncrona ligue de volta. Um retorno de chamada síncrona é fornecido para outra função que vai fazer alguma tarefa e, em seguida, retornar para o chamador com a tarefa concluída. Um retorno de chamada assíncrono é fornecido para outra função que vai começar uma tarefa e, em seguida, retornar para o chamador com a tarefa possivelmente não concluída.

Uma chamada de retorno síncrono é tipicamente utilizada para fornecer um representante para uma outra função à qual os outros delegados função algum passo da tarefa. Exemplos clássicos de esta delegação são funções bsearch() e qsort() da biblioteca padrão C. Ambas as funções tomar um callback que é usado durante a tarefa a função é fornecer para que o tipo do ser dados procurou, no caso de bsearch(), ou classificadas, no caso de qsort(), não precisa ser conhecido pelo função que está sendo usado.

Por exemplo aqui é um programa de amostra pequena, com bsearch() utilizando diferentes funções de comparação, retornos de chamada síncronos. Por que nos permite delegar a comparação de dados para uma função de retorno de chamada, a função bsearch() nos permite decidir em tempo de execução que tipo de comparação que queremos utilizar. Esta é síncrono porque quando a função bsearch() retorna a tarefa está completa.

#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);
    }
}

Uma chamada de retorno assíncrona é diferente em que, quando a função chamada para que nós fornecemos uma chamada de retorno retornos, a tarefa pode não ser concluída. Este tipo de retorno de chamada é geralmente usado com I / O assíncrona em que uma operação de I / O é iniciado e, em seguida, quando estiver concluída, o retorno de chamada é invocado.

No programa seguinte, criamos um soquete para escutar as solicitações de conexão TCP e quando uma solicitação é recebida, a função de fazer a escuta, em seguida, chama a função de retorno de chamada fornecido. Esta aplicação simples pode ser exercida por executá-lo em uma janela enquanto estiver usando o utilitário telnet ou um navegador da Web para tentar se conectar em outra janela.

Eu levantei a maioria do código WinSock do exemplo Microsoft fornece com a função accept() em https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526 (v = VS.85) .aspx

Esta aplicação inicia uma listen() no host local, 127.0.0.1, usando a porta 8282 para que você possa usar qualquer telnet 127.0.0.1 8282 ou http://127.0.0.1:8282/.

Esta aplicação de exemplo foi criado como um aplicativo de console com Visual Studio 2017 Community Edition e está usando a versão Microsoft WinSock de soquetes. Para uma aplicação Linux as funções Winsock precisaria ser substituído com as alternativas Linux e biblioteca do Windows threads usaria pthreads vez.

#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);
}

Normalmente, isso pode ser feito usando um ponteiro de função, que é uma variável especial que aponta para o local de memória de uma função. então você pode usar isso para chamar a função com argumentos específicos. Então, provavelmente haverá uma função que define a função de retorno. Isto irá aceitar um ponteiro de função e, em seguida, armazenar que em algum lugar endereço onde ele pode ser usado. Depois disso, quando o evento especificado é acionado, ele irá chamar essa função.

É muito mais fácil de compreender uma idéia através do exemplo. O que foi dito sobre a função de retorno de chamada em C até agora são grandes respostas, mas provavelmente a maior vantagem de usar o recurso é manter o código limpo e organizado.

Exemplo

Os seguintes implementos código C rápida triagem. A linha mais interessante no código a seguir é este, onde podemos ver a função de retorno em ação:

qsort(arr,N,sizeof(int),compare_s2b);

O compare_s2b é o nome da função que qsort () está usando para chamar a função. Isso mantém qsort (), de modo organizado (daí mais fácil de manter). Você acabou de chamar uma função pelo nome de dentro de outra função (é claro, a declaração do protótipo função, pelo menos, deve precde antes que possa ser chamado de outra função).

O código completo

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

int arr[]={56,90,45,1234,12,3,7,18};
//function prototype declaration 

int compare_s2b(const void *a,const void *b);

int compare_b2s(const void *a,const void *b);

//arranges the array number from the smallest to the biggest
int compare_s2b(const void* a, const void* b)
{
    const int* p=(const int*)a;
    const int* q=(const int*)b;

    return *p-*q;
}

//arranges the array number from the biggest to the smallest
int compare_b2s(const void* a, const void* b)
{
    const int* p=(const int*)a;
    const int* q=(const int*)b;

    return *q-*p;
}

int main()
{
    printf("Before sorting\n\n");

    int N=sizeof(arr)/sizeof(int);

    for(int i=0;i<N;i++)
    {
        printf("%d\t",arr[i]);
    }

    printf("\n");

    qsort(arr,N,sizeof(int),compare_s2b);

    printf("\nSorted small to big\n\n");

    for(int j=0;j<N;j++)
    {
        printf("%d\t",arr[j]);
    }

    qsort(arr,N,sizeof(int),compare_b2s);

    printf("\nSorted big to small\n\n");

    for(int j=0;j<N;j++)
    {
        printf("%d\t",arr[j]);
    }

    exit(0);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top