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

Da leitura que eu fiz, o Core Audio depende muito de callbacks (e C ++, mas isso é outra história).

Eu entendo o conceito (tipo de) de configurar uma function que é chamada por outra function repetidamente para realizar uma tarefa. Eu simplesmente não entendo como eles são configurados e como eles realmente funcionam. Qualquer exemplo seria apreciado.

Não há “callback” em C – não mais do que qualquer outro conceito genérico de programação.

Eles são implementados usando pointers de function. Aqui está um exemplo:

void populate_array(int *array, size_t arraySize, int (*getNextValue)(void)) { for (size_t i=0; i 

Aqui, a function populate_array usa um ponteiro de function como seu terceiro parâmetro e o chama para obter os valores para preencher a matriz. getNextRandomValue o retorno de chamada getNextRandomValue , que retorna um valor random e passou um ponteiro para ele para populate_array . populate_array chamará nossa function de retorno de chamada 10 vezes e atribuirá os valores retornados aos elementos na matriz especificada.

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

Digamos que você queira escrever algum código que permita o registro de chamadas de retorno quando algum evento ocorrer.

Primeiro defina o tipo de function usada para o retorno de chamada:

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

Agora, defina uma function que é usada para registrar um retorno de chamada:

 int event_cb_register(event_cb_t cb, void *userdata); 

Este é o código que se parece com o que registra um retorno de chamada:

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

Nos internos do despachante de events, o retorno de chamada pode ser armazenado em uma estrutura semelhante a esta:

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

Isto é o que o código parece que executa um retorno de chamada.

 struct event_cb *callback; ... /* Get the event_cb that you want to execute */ callback->cb(event, callback->data); 

Um simples programa de retorno de chamadas. Espero que responda a sua pergunta.

 #include  #include  #include  #include  #include  #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 \tb:%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 function de retorno de chamada em C é o equivalente a uma variável / parâmetro de function atribuída para ser usada dentro de outra function. Exemplo de Wiki

No código abaixo,

 #include  #include  /* 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 function (* numberSource) dentro da chamada de function PrintTwoNumbers é uma function para “chamar de volta” / executar de dentro de PrintTwoNumbers como ditado pelo código enquanto é executado.

Então, se você tivesse algo como uma function pthread, poderia atribuir outra function para executar dentro do loop a partir de sua instanciação.

Retornos de chamada em C geralmente são implementados usando pointers de function e um ponteiro de dados associado. Você passa sua function on_event() e pointers de dados para uma function de estrutura watch_events() (por exemplo). Quando um evento acontece, sua function é chamada com seus dados e alguns dados específicos do evento.

Callbacks também são usados ​​na programação da GUI. O tutorial do GTK + tem uma boa seção sobre a teoria dos sinais e retornos de chamada .

Este artigo da wikipedia tem um exemplo em C.

Um bom exemplo é que novos módulos escritos para aumentar o registro do servidor da Web Apache com o processo principal do apache, passando-os aos pointers de function para que essas funções sejam chamadas de volta para processar solicitações de páginas da web.

Geralmente isso pode ser feito usando um ponteiro de function, que é uma variável especial que aponta para o local da memory de uma function. Você pode então usar isso para chamar a function com argumentos específicos. Portanto, provavelmente haverá uma function que define a function de retorno de chamada. Isso aceitará um ponteiro de function e armazenará esse endereço em algum lugar onde possa ser usado. Depois disso, quando o evento especificado for acionado, ele chamará essa function.

Um retorno de chamada em C é uma function que é fornecida para outra function para “chamar de volta para” em algum momento quando a outra function está fazendo sua tarefa.

Há duas maneiras de usar um retorno de chamada : retorno de chamada síncrono e retorno de chamada asynchronous. Um retorno de chamada síncrono é fornecido para outra function que vai executar alguma tarefa e, em seguida, retorna ao responsável pela chamada com a tarefa concluída. Um retorno de chamada asynchronous é fornecido para outra function que iniciará uma tarefa e retornará ao responsável pela chamada com a tarefa possivelmente não concluída.

Normalmente, um retorno de chamada síncrono é usado para fornecer um delegado a outra function para a qual a outra function delega alguma etapa da tarefa. Exemplos clássicos desta delegação são as funções bsearch() e qsort() da Standard C Library. Ambas as funções recebem um retorno de chamada que é usado durante a tarefa que a function está fornecendo para que o tipo de dados sendo pesquisado, no caso de bsearch() , ou classificado, no caso de qsort (), não precise ser conhecido pela function que está sendo usada.

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

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

Um retorno de chamada asynchronous é diferente quando a function chamada para a qual fornecemos um retorno de chamada retorna, a tarefa pode não ser concluída. Esse tipo de retorno de chamada é geralmente usado com E / S assíncrona na qual uma operação de E / S é iniciada e, quando ela é concluída, o retorno de chamada é chamado.

No programa a seguir, criamos um soquete para escutar solicitações de conexão TCP e, quando uma solicitação é recebida, a function que faz a escuta chama a function de retorno de chamada fornecida. Este aplicativo simples pode ser exercitado, executando-o em uma janela enquanto estiver usando o utilitário telnet ou um navegador da web para tentar se conectar em outra janela.

Eu tirei a maior parte do código WinSock do exemplo que a Microsoft fornece com a function accept() em https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx

Este aplicativo inicia um listen() no host local, 127.0.0.1, usando a porta 8282 para que você possa usar o telnet 127.0.0.1 8282 ou http://127.0.0.1:8282/ .

Este aplicativo de exemplo foi criado como um aplicativo de console com o Visual Studio 2017 Community Edition e está usando a versão de sockets do Microsoft WinSock. Para um aplicativo Linux, as funções do WinSock precisariam ser substituídas pelas alternativas do Linux e a biblioteca de threads do Windows usaria o pthreads .

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