Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the use of userdata in callback register function in c?

In the register callback function there are two arguments. One is the function pointer, and second is userdata.

int callback_register(fn_ptr cb, void *userdata);
//fn_ptr is typedef

During callback the same userdata is sent back as an argument. I understand the use of sending a function pointer, but not of sending userdata. Can anyone please tell how this is used?

like image 321
Omkar Kekre Avatar asked Jun 15 '18 10:06

Omkar Kekre


People also ask

How do you register a callback in C++?

To register the callback there will be a function where A can pass the pointer A will call the function as B has stored the address (which the function pointer points to) and can use it to call the function later. When B calls the function through the function pointer it is called as callback.

What is a callback in C++?

A callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time [Source : Wiki ]. In simple language, If a reference of a function is passed to another function as an argument to call it, then it will be called as a Callback function.

How do callback functions work in JavaScript?

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.

What is a 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.


1 Answers

If all you have is a function pointer pointing at the callback function, there's no way to pass extra data down into the callback function.

For example, suppose I have some strings I want to sort using qsort. I might start out with some simple code like this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define Sizeofarray(a) (sizeof(a) / sizeof(*a))

int compar(const void *, const void *);

int main()
{
    char *data[] = { "apple", "pear", "1", "2", "10" };
    int i;

    printf("unsorted:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);

    qsort(data, Sizeofarray(data), sizeof(*data), compar);

    printf("\nsorted:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);
}

int compar(const void *p1, const void *p2)
{
    const char *s1 = *(const char **)p1;
    const char *s2 = *(const char **)p2;
    return strcmp(s1, s2);
}

This program works fine, and it prints:

unsorted:
apple
pear
1
2
10

sorted:
1
10
2
apple
pear

But that's an alphabetic sort, using strcmp. Suppose I want the option of sorting numerically (that is, like the standard Unix/Linux sort command with its -n option). And, furthermore, suppose I truly want to make it an option, controlled by a run-time variable. I could write a new, slightly more complicated comparison function, like this:

int compar2(const void *p1, const void *p2)
{
    const char *s1 = *(const char **)p1;
    const char *s2 = *(const char **)p2;

    if(numeric)
         return atoi(s1) > atoi(s2);
    else return strcmp(s1, s2);
}

With the numeric flag set to true, the input now sorts as

apple
pear
1
2
10

(The words come first because atoi "converts" them to 0.)

But then the vital question is, where does that numeric flag come from? If all I have is qsort and a callback function that takes no user-specifiable context, I have no choice but to make the numeric flag a global variable:

int numeric;

int main()
{
    char *data[] = { "apple", "pear", "1", "2", "10" };
    int i;

    printf("unsorted:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);

    numeric = 0;
    qsort(data, Sizeofarray(data), sizeof(*data), compar2);

    printf("\nsorted alphabetically:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);

    numeric = 1;
    qsort(data, Sizeofarray(data), sizeof(*data), compar2);

    printf("\nsorted numerically:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);
}

And this works, too. But of course nobody likes global variables.

So that's where the concept of a "userdata" parameter comes in. I don't know how standard it is, but my system provides a qsort variant called qsort_r. (The "r" is for "reentrant".) With this version, the comparison function is passed an extra argument, that I can do whatever I want with. Here, I can make it a pointer to my numeric flag, and now my numeric flag doesn't have to be a global variable:

int compar3(void *, const void *, const void *);

int main()
{
    char *data[] = { "apple", "pear", "1", "2", "10" };
    int i;
    int numeric;

    printf("unsorted:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);

    numeric = 0;
    qsort_r(data, Sizeofarray(data), sizeof(*data), &numeric, compar3);

    printf("\nsorted alphabetically:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);

    numeric = 1;
    qsort_r(data, Sizeofarray(data), sizeof(*data), &numeric, compar3);

    printf("\nsorted numerically:\n");
    for(i = 0; i < Sizeofarray(data); i++) printf("%s\n", data[i]);
}

int compar3(void *userdata, const void *p1, const void *p2)
{
    int numeric = *(int *)userdata;
    const char *s1 = *(const char **)p1;
    const char *s2 = *(const char **)p2;

    if(numeric)
         return atoi(s1) > atoi(s2);
    else return strcmp(s1, s2);
}

So, in a nutshell, the answer to the question "What is the use of a userdata argument in a callback function?" is "So that calling functions don't have to use global variables to pass extra context information down into their callback functions."


Footnote: in this case, I could have taken a different approach. I could have defined two different compar functions, one for alphabetic data, one for numeric. I could have passed one function or the other to qsort, like this:

qsort(data, Sizeofarray(data), sizeof(*data), numeric ? compar_num : compar_alph);

That way I wouldn't have needed a userdata pointer, or qsort_r, or a global variable, at all. But I hope this example has demonstrated how a userdata pointer can be useful, and how to use one.

like image 137
Steve Summit Avatar answered Sep 28 '22 04:09

Steve Summit



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!