Un callback in C è una funzione che viene fornita a un'altra funzione per "richiamare" ad un certo punto quando l'altra funzione sta facendo il suo compito.
Esistono due modi in cui viene utilizzato un callback : callback sincrono e callback asincrono. Un callback sincrono viene fornito a un'altra funzione che eseguirà alcune attività e quindi tornerà al chiamante con l'attività completata. Un callback asincrono viene fornito a un'altra funzione che avvia un'attività e quindi ritorna al chiamante con l'attività che probabilmente non è stata completata.
Un callback sincrono viene in genere utilizzato per fornire un delegato a un'altra funzione a cui l'altra funzione delega un passaggio dell'attività. Esempi classici di questa delegazione sono le funzioni bsearch()
e qsort()
dalla libreria standard C. Entrambe queste funzioni accettano un callback che viene utilizzato durante l'attività fornita dalla funzione in modo tale che il tipo di dati ricercati, nel caso bsearch()
o ordinati, nel caso di qsort()
, non debba essere conosciuto dalla funzione Usato.
Ad esempio, ecco un piccolo programma di esempio che bsearch()
utilizza diverse funzioni di confronto, richiamate sincrone. Consentendoci di delegare il confronto dei dati a una funzione di callback, la bsearch()
funzione ci consente di decidere in fase di esecuzione quale tipo di confronto vogliamo usare. Questo è sincrono perché quando la bsearch()
funzione restituisce l'attività è 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);
}
}
Un callback asincrono è diverso dal fatto che quando la funzione chiamata a cui forniamo un callback ritorna, l'attività potrebbe non essere completata. Questo tipo di callback viene spesso utilizzato con l'I / O asincrono in cui viene avviata un'operazione di I / O e quindi al termine viene richiamato il callback.
Nel seguente programma creiamo un socket per ascoltare le richieste di connessione TCP e quando viene ricevuta una richiesta, la funzione che fa l'ascolto invoca quindi la funzione di callback fornita. Questa semplice applicazione può essere esercitata eseguendola in una finestra mentre si utilizza l' telnet
utilità o un browser Web per tentare di connettersi in un'altra finestra.
Ho estratto la maggior parte del codice WinSock dall'esempio fornito da Microsoft con la accept()
funzione su https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx
Questa applicazione avvia un listen()
host locale, 127.0.0.1, usando la porta 8282 in modo da poter usare telnet 127.0.0.1 8282
o http://127.0.0.1:8282/
.
Questa applicazione di esempio è stata creata come un'applicazione console con Visual Studio 2017 Community Edition e utilizza la versione di socket Microsoft WinSock. Per un'applicazione Linux le funzioni di WinSock dovrebbero essere sostituite con le alternative di Linux e la libreria di thread di Windows dovrebbe pthreads
invece utilizzare .
#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);
}