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?
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With