¿Qué es una “callback” en C y cómo se implementan?

Por la lectura que he hecho, Core Audio depende en gran medida de devoluciones de llamadas (y C ++, pero esa es otra historia).

Entiendo el concepto (tipo de) de configurar una función que otra función llama repetidamente para realizar una tarea. Simplemente no entiendo cómo se configuran y cómo funcionan en realidad. Cualquier ejemplo sería apreciado.

No hay “callback” en C, no más que cualquier otro concepto de progtwigción genérico.

Se implementan usando punteros de función. Aquí hay un ejemplo:

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

Aquí, la función populate_array toma un puntero de función como su tercer parámetro y lo llama para obtener los valores con los que poblar la matriz. Hemos escrito la callback getNextRandomValue , que devuelve un valor aleatorio, y le pasamos un puntero a populate_array . populate_array llamará a nuestra función de callback 10 veces y asignará los valores devueltos a los elementos en la matriz dada.

Aquí hay un ejemplo de devoluciones de llamada en C.

Digamos que desea escribir un código que permita llamar a las devoluciones de llamada cuando se produce algún evento.

Primero defina el tipo de función utilizada para la callback:

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

Ahora, defina una función que se usa para registrar una callback:

 int event_cb_register(event_cb_t cb, void *userdata); 

Así es como se vería el código que registra una 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); ... 

En las partes internas del despachador de eventos, la callback puede almacenarse en una estructura que se parece a esto:

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

Este es el aspecto del código que ejecuta una callback.

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

Un simple progtwig de callback. Espero que responda tu pregunta.

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

Una función de callback en C es el equivalente de un parámetro / variable de función asignado para ser utilizado dentro de otra función. Ejemplo de Wiki

En el código a continuación,

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

La función (* numberSource) dentro de la función llamada PrintTwoNumbers es una función para “devolver la llamada” / ejecutar desde dentro de PrintTwoNumbers según lo dicte el código mientras se ejecuta.

Entonces, si tuviera algo así como una función pthread, podría asignar otra función para ejecutar dentro del ciclo desde su creación de instancias.

Las rellamadas en C generalmente se implementan usando indicadores de función y un puntero de datos asociado. Usted pasa su función on_event() y punteros de datos a una función de marco watch_events() (por ejemplo). Cuando ocurre un evento, se llama a su función con sus datos y algunos datos específicos del evento.

Las devoluciones de llamada también se utilizan en la progtwigción de GUI. El tutorial GTK + tiene una buena sección sobre la teoría de señales y devoluciones de llamada .

Este artículo de wikipedia tiene un ejemplo en C.

Un buen ejemplo es que los nuevos módulos escritos para boost el registro del servidor web Apache con el proceso principal de apache pasándole punteros a funciones para que esas funciones se vuelvan a llamar para procesar las solicitudes de la página web.

Por lo general, esto se puede hacer utilizando un puntero a la función, que es una variable especial que apunta a la ubicación de la memoria de una función. A continuación, puede usar esto para llamar a la función con argumentos específicos. Entonces, probablemente habrá una función que establece la función de callback. Esto aceptará un puntero de función y luego almacenará esa dirección en algún lugar donde pueda usarse. Después de eso, cuando se desencadena el evento especificado, llamará a esa función.

Una callback en C es una función que se proporciona a otra función para “volver a llamar” en algún momento cuando la otra función está haciendo su tarea.

Hay dos formas en que se utiliza una callback : callback sincrónica y callback asincrónica. Se proporciona una callback sincrónica a otra función que realizará alguna tarea y luego regresará a la persona que llama con la tarea completada. Se proporciona una callback asincrónica a otra función que iniciará una tarea y luego regresará a la persona que llama con la tarea posiblemente no completada.

Una callback sincrónica se utiliza normalmente para proporcionar un delegado a otra función a la que la otra función delega algún paso de la tarea. Los ejemplos clásicos de esta delegación son las funciones bsearch() y qsort() de la biblioteca estándar C. Ambas funciones toman una callback que se utiliza durante la tarea que proporciona la función para que el tipo de datos que se buscan, en el caso de bsearch() u ordenado, en el caso de `qsort (), no necesite ser conocido por la función que se está utilizando.

Por ejemplo, aquí hay un pequeño progtwig de muestra con bsearch() usa diferentes funciones de comparación, devoluciones de llamada sincrónicas. Al permitirnos delegar la comparación de datos a una función de callback, la bsearch() nos permite decidir en tiempo de ejecución qué tipo de comparación queremos usar. Esto es sincrónico porque cuando la bsearch() devuelve la tarea se 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); } } 

Una callback asincrónica es diferente porque cuando la función llamada a la que proporcionamos una callback devuelve, la tarea puede no completarse. Este tipo de callback a menudo se usa con E / S asíncronas en las que se inicia una operación de E / S y luego, cuando se completa, se invoca la callback.

En el siguiente progtwig creamos un socket para escuchar las solicitudes de conexión TCP y cuando se recibe una solicitud, la función que realiza la escucha invoca la función de callback provista. Esta simple aplicación se puede ejecutar ejecutándola en una ventana mientras se usa la utilidad telnet o un navegador web para intentar conectarse en otra ventana.

Levanté la mayor parte del código de WinSock del ejemplo que proporciona Microsoft con la función accept() en https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx

Esta aplicación inicia listen() en el host local, 127.0.0.1, utilizando el puerto 8282 para que pueda usar telnet 127.0.0.1 8282 o http://127.0.0.1:8282/ .

Esta aplicación de muestra se creó como una aplicación de consola con Visual Studio 2017 Community Edition y está utilizando la versión de sockets de Microsoft WinSock. Para una aplicación de Linux, las funciones de WinSock deberían reemplazarse con las alternativas de Linux y la biblioteca de hilos de Windows usaría pthreads lugar.

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