Reputation: 3043
From the reading that I have done, Core Audio relies heavily on callbacks (and C++, but that's another story).
I understand the concept (sort of) of setting up a function that is called by another function repeatedly to accomplish a task. I just don't understand how they get set up and how they actually work. Any examples would be appreciated.
Upvotes: 193
Views: 386857
Reputation: 143
Since I forgot the exact format for dynamic callbacks and also could not find it here, I will leave this comment for future reference. Hope it helps others.
First of all: there is no "callback" concept in the C standard, as explained by @aib and others. All are just function pointers but since they are universally called callbacks I will do that as well.
@Richard-Chambers explained about Synchronous and Asynchronous callbacks.
Static & Dynamic callbacks
Static and Dynamic callbacks are both forms of asynchronous callbacks;
Static callbacks
The purpose of a static callback is to register a handler function to the function pointer only once and which is called each time that event happens. This type of callback has nothing to do with the static keyword.
The example from @Russell-Bryant gives a good example for atatic callbacks: First define the type of function used for the callback.
! Note that the input variables/structures do not need to be const, this depends on the use-case.
typedef void (*type_name)(<function input as normal>);
typedef void (*event_cb_t)(const struct event *evt, void *userdata);
Of course also a declaration of the (private/public) global function pointer.
! This can also be inside a global (context) structure.
event_cb_t keyboard_input_cb; // might be in another global structure.
// and optional a pointer to user data:
void* my_data;
The register function:
int event_cb_register(event_cb_t cb, void *userdata)
{
//... error checking?
keyboard_input_cb = cb;
my_data = userdata;
}
And then also the definition of the callback/event handler itself (my_event_cb) and the call to the register function to set those pointers as in Russell's example.
Dynamic callbacks
The purpose for dynamic callbacks is different: this pointer can be changed from multiple parts in the code. A typical application is a network/protocol stack where each command/packet gets it's own callback to a specific handler function.
Variant 1: different handle as data structure
When the handle/structure that holds the callback/pointer is different than the function's input structure, the code structure is much the same as the static callback. The typedef is the same:
typedef void (*event_cb_t)(const struct event *evt, void *userdata);
But the pointer is stored in a dynamic structure:
typedef struct
{
event_cb_t packet_cb;
uint8_t parameter;
//... other command flags/variables
} packet_handle_t;
And there are some api functions that set-up different commands/packets/interfaces.
int packet_command_raw(packet_handle_t *hpacket)
{
// check if interface/fifo is ready to receive a packet.
//... error checking, command set-up, etc.
memcpy(fifo[i], hpacket, sizeof(hpacket));
// The stack will call the callback inside hpacket when needed.
return NO_ERROR;
}
int packet_command_1(uint8_t byte1, event_cb_t callback)
{
// check if interface/fifo is ready to receive a packet.
//... error checking, command set-up, etc.
fifo[i].parameter = byte1;
fifo[i].packet_cb = callback;
// The stack will call the callback inside hpacket when needed.
return NO_ERROR;
}
Variant 2: same handle as data structure
When the handle/structure that holds the callback/pointer is the same as the function's input structure/handle, the pointer is self contained but a trick is needed.
This trick is to define an internal name for the structure before the structure contents are defined. (__packet_handle_t)
This is needed when the event handlers, stored in the packet handle, also require the packet handle itself as input.
typedef struct __packet_handle_t
{
uint8_t parameter;
void (* event1)(struct __packet_handle_t *hpacket);
void (* event2)(struct __packet_handle_t *hpacket);
} packet_handle_t;
In this example on both types of event a different handler is called, both with the handle as input.
The pointers are lost when the packet has been handled (fifo entry is cleared).
This kind of handle structure can also be found in static callbacks.
A typical use-case for that are peripheral HAL handles that contain one or more event callbacks that require the peripheral handle as input.
Upvotes: 1
Reputation: 17713
A callback in C is a function that is provided to another function to "call back to" at some point when the other function is doing its task.
There are two ways that a callback is used: synchronous callback and asynchronous callback. A synchronous callback is provided to another function which is going to do some task and then return to the caller with the task completed. An asynchronous callback is provided to another function which is going to start a task and then return to the caller with the task possibly not completed.
Synchronous callback
A synchronous callback is typically used to provide a delegate to another function to which the other function delegates some step of the task. Classic examples of this delegation are the functions bsearch()
and qsort()
from the C Standard Library. Both of these functions take a callback which is used during the task the function is providing so that the type of the data being searched, in the case of bsearch()
, or sorted, in the case of qsort()
, does not need to be known by the function being used.
For example, here is a small sample program with bsearch()
using different comparison functions, demonstrating synchronous callbacks. By allowing us to delegate the data comparison to a callback function, the bsearch()
function allows us to decide at run time what kind of comparison we want to use. This is synchronous because when the bsearch()
function returns the task is complete.
#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);
}
}
Asynchronous callback
An asynchronous callback is different in that when the called function to which we provide a callback returns, the task may not be completed. This type of callback is often used with asynchronous I/O in which an I/O operation is started and then when it is completed, the callback is invoked.
In the following program we create a socket to listen for TCP connection requests and when a request is received, the function doing the listening then invokes the callback function provided. This simple application can be exercised by running it in one window while using the telnet
utility or a web browser to attempt to connect in another window.
I lifted most of the WinSock code from the example Microsoft provides with the accept()
function at https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx
This application starts a listen()
on the local host, 127.0.0.1, using port 8282 so you could use either telnet 127.0.0.1 8282
or http://127.0.0.1:8282/
.
This sample application was created as a console application with Visual Studio 2017 Community Edition and it is using the Microsoft WinSock version of sockets. For a Linux application the WinSock functions would need to be replaced with the Linux alternatives and the Windows threads library would use pthreads
instead.
#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);
}
Upvotes: 13
Reputation: 742
It is lot easier to understand an idea through example. What have been told about callback function in C so far are great answers, but probably the biggest benefit of using the feature is to keep the code clean and uncluttered.
The following C code implements quick sorting. The most interesting line in the code below is this one, where we can see the callback function in action:
qsort(arr,N,sizeof(int),compare_s2b);
The compare_s2b is the name of function which qsort() is using to call the function. This keeps qsort() so uncluttered (hence easier to maintain). You just call a function by name from inside another function (of course, the function prototype declaration, at the least, must precde before it can be called from another function).
#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);
}
Upvotes: 2
Reputation: 305
A callback function in C is the equivalent of a function parameter / variable assigned to be used within another function.Wiki Example
In the code below,
#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;
}
The function (*numberSource) inside the function call PrintTwoNumbers is a function to "call back" / execute from inside PrintTwoNumbers as dictated by the code as it runs.
So if you had something like a pthread function you could assign another function to run inside the loop from its instantiation.
Upvotes: 12
Reputation: 1763
A simple call back program. Hope it answers your question.
#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;
}
Upvotes: 29
Reputation: 47041
There is no "callback" in C - not more than any other generic programming concept.
They're implemented using function pointers. Here's an example:
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);
...
}
Here, the populate_array
function takes a function pointer as its third parameter, and calls it to get the values to populate the array with. We've written the callback getNextRandomValue
, which returns a random-ish value, and passed a pointer to it to populate_array
. populate_array
will call our callback function 10 times and assign the returned values to the elements in the given array.
Upvotes: 253
Reputation: 1911
Here is an example of callbacks in C.
Let's say you want to write some code that allows registering callbacks to be called when some event occurs.
First define the type of function used for the callback:
typedef void (*event_cb_t)(const struct event *evt, void *userdata);
Now, define a function that is used to register a callback:
int event_cb_register(event_cb_t cb, void *userdata);
This is what code would look like that registers a 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);
...
In the internals of the event dispatcher, the callback may be stored in a struct that looks something like this:
struct event_cb {
event_cb_t cb;
void *data;
};
This is what the code looks like that executes a callback.
struct event_cb *callback;
...
/* Get the event_cb that you want to execute */
callback->cb(event, callback->data);
Upvotes: 155
Reputation: 13797
This wikipedia article has an example in C.
A good example is that new modules written to augment the Apache Web server register with the main apache process by passing them function pointers so those functions are called back to process web page requests.
Upvotes: 2
Reputation: 5252
Usually this can be done by using a function pointer, that is a special variable that points to the memory location of a function. You can then use this to call the function with specific arguments. So there will probably be a function that sets the callback function. This will accept a function pointer and then store that address somewhere where it can be used. After that when the specified event is triggered, it will call that function.
Upvotes: 0
Reputation: 201056
Callbacks in C are usually implemented using function pointers and an associated data pointer. You pass your function on_event()
and data pointers to a framework function watch_events()
(for example). When an event happens, your function is called with your data and some event-specific data.
Callbacks are also used in GUI programming. The GTK+ tutorial has a nice section on the theory of signals and callbacks.
Upvotes: 4