What is a "callback" in C and how are they implemented?

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.

Answers


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.


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

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

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.


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.


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.


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.

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 instance here is a small sample program with bsearch() using different comparison functions, 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);
    }
}

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

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.


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.

Example

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

The Complete Code

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

Need Your Help

How to define restrictions between two elements in XSD

xml xsd xsd-validation

Is it possible to define value restrictions between two elements in XML Schema?