Passing a parameter in a callback in C [duplicate] - c

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do function pointers in C work?
Surfing on stackoverflow I found this example:
/* Validation functions start */
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);
...
}
I was wondering, imagine getNextRandomValue had a parameter getNextRandomValue(int i), how would I include this and making the function accepting inputs?
Many thanks

Common practice is to pass a pointer to "data" together with the function. When function gets called, pass that "data" pointer into function and assume that the function itself knows what to do with that data. In fact the data is usually a pointer to a structure. So the code looks like this:
struct func1_data {
int a;
int b;
};
struct func2_data {
char x[10];
};
int function1(void *data) {
struct func1_data *my_data = (typeof(my_data)) data;
/* do something with my_data->a and my_data->b */
return result;
}
int function2(void *data) {
struct func2_data *my_data = (typeof(my_data)) data;
/* do something with my_data->x */
return result;
}
and assume we have
int caller(int (*callback), void *data) {
return callback(data);
}
Then you call all this like this:
struct func1_data data1 = { 5, 7 };
struct func2_data data2 = { "hello!" };
caller(function1, (void *) &data1);
caller(function2, (void *) &data2);

It's probably a good idea to get familiar with function-pointer syntax. You need to change the argument to int (*getNextValue)(int).

Then your code should be like this...
void populate_array(int *array, size_t arraySize, int (*getNextValue)(unsigned int))
{
unsigned int seedvalue = 100;
for (size_t i=0; i<arraySize; i++)
array[i] = getNextValue(seedvalue);
}
int getNextRandomValue(unsigned int seed)
{
srand(seed);
return rand();
}
int main(void)
{
int myarray[10];
populate_array(myarray, 10, getNextRandomValue);
...
}

Related

Swapping struct array elements

I have difficulty applying the pass by reference and pass by value separation in structs.How can I swap the elements of the fixed size struct array as below.
struct try{
int num;
char name[10];
};
int main(){
struct try book[3];
void swapper(/********/);// <-what should be the argument of this function
}
void swapper(/********/){//swap second and third element of struct array
/*how the swap may be done?
temp=book[2];
book[2]=book[3];
temp=book[3];*/
}
There are a lot of ways to do what you're asking. One approach:
#include <stdio.h>
struct try {
int num;
char name[10];
};
void
swapper(struct try *a, int b, int c)
{
struct try tmp = a[b];
a[b] = a[c];
a[c] = tmp;
}
void
display(const struct try *t, size_t count)
{
while( count-- ){
printf("%d: %s\n", t->num, t->name);
t += 1;
}
}
int
main(void) {
struct try book[] = {
{ 1, "foo"},
{ 2, "bar"},
{ 3, "baz"}
};
display(book, sizeof book / sizeof *book);
swapper(book, 1, 2);
display(book, sizeof book / sizeof *book);
return 0;
}

Parameter passing multiple values using void pointer

I want to pass multiple arguments to a function using a void pointer.
void* function(void *params)
{
//casting pointers
//doing something
}
int main()
{
int a = 0
int b = 10;
char x = 'S';
void function(???);
return 0;
}
I know that I have to cast them to a certain variable in my function but I do not know how I can pass my 3 arguments as one void pointer to my function.
I have searched for this problem know quite some time but I could not find anything that would help me.
You could do it like this:
struct my_struct
{
int a;
int b;
char x;
}
void * function(void * pv)
{
struct my_strcut * ps = pv; /* Implicitly converting the void-pointer
/* passed in to a pointer to a struct. */
/* Use ps->a, ps->b and ps->x here. */
return ...; /* NULL or any pointer value valid outside this function */
}
Use it like this
int main(void)
{
struct my_struct s = {42, -1, 'A'};
void * pv = function(&s);
}
Following up on the OP's update:
struct my_struct_foo
{
void * pv1;
void * pv2;
}
struct my_struct_bar
{
int a;
int b;
}
void * function(void * pv)
{
struct my_strcut_foo * ps_foo = pv;
struct my_struct_bar * ps_bar = ps_foo->pv1;
/* Use ps_foo->..., ps_bar->... here. */
return ...; /* NULL or any pointer value valid outside this function */
}
Use it like this
int main(void)
{
struct my_struct_bar s_bar = {42, -1};
struct my_struct_foo s_foo = {&s_bar, NULL};
void * pv = function(&s_foo);
}
The void* is used as a pointer to a "generic" type. Hence, you need to create a wrapping type, cast convert to void* to invoke the function, and cast convert back to your type in the function's body.
#include <stdio.h>
struct args { int a, b; char X; };
void function(void *params)
{
struct args *arg = params;
printf("%d\n", arg->b);
}
int main()
{
struct args prm;
prm.a = 0;
prm.b = 10;
prm.X = 'S';
function(&prm);
return 0;
}

Can we use void pointer of arrays

I am just working on a liberty functions in which we define our own datatypes for student and book I have to write a code which finds student by id and book by id these are the two functions. In this functions the pointers which I pass are different but the logic is the same so I got a though that why can't we write one function and pass which thing we want. I mean when we pass the student list it will return the index of student when we pass the book list it will return the book index of the book. Can we use void pointers for that??? Thank you everyone!!!
int findBookId(Book* booklist,int* bcount,unsigned int* tbid)
{
int i;
for (i=0; i<*bcount; i++)
{
if (booklist[i].id==*tbid)
{
return i;
}
}
return NOT_FOUND;
}
int findStuId(Student* stulist,int* scount,unsigned int* tsid)
{
int i;
for (i=0; i<*scount; i++)
{
if (stulist[i].id==*tsid)
{
return i;
}
}
return NOT_FOUND;
}
Assuming you have a student structure:
struct student {
int id;
char name[20];
};
You can imitate qsort() function, to design a parameter to receive a callback function and to receive the size and size of each element if you'd like use void *.
int find_ele(void *base, size_t num, size_t width,
int (*equal)(const void *, const void *),
void *param)
{
int i;
for (i = 0; i < num; ++i) {
if (equal((char *) base + i * width, param)) {
return i;
}
}
return -1;
}
Then, define a "tester":
int student_tester(const void *p1, const void *p2)
{
struct student *sp = (struct student *) p1;
int id = *(int *) p2;
return sp->id == id;
}
In main() function:
int main(void)
{
struct student student_list[] = {
0, "A",
1, "B",
2, "C"
};
int id = 2;
int index = find_ele(student_list, sizeof student_list,
sizeof(struct student), student_tester, &id);
if (index != -1) {
printf("find_ele(id=2) = student_list[%d]; name = %s. \n",
index, student_list[index].name);
} else {
printf("Not found. \n");
}
return 0;
}
This is a bit complicated. You can create macros to simplify it if you don't care.
Rename find_ele to _find_ele, and create a macro:
#define find_ele(base, num, compare, param) _find_ele(base, \
num / sizeof base[0], \
sizeof base[0], \
compare, param)
And create another macro to define a "tester":
#define define_tester(name, type, type_to_find, code) \
int name(const void *_p, const void *param) { \
type *p = (type *) _p; \
type_to_find value = *(type_to_find *) param; \
return (code); \
}
Now you can define a "tester" like this:
define_tester(student_tester, struct student, int,
p->id == value);
Complete code:
#include <stdio.h>
int _find_ele(void *base, size_t num, size_t width,
int (*equal)(const void *, const void *),
void *param)
{
int i;
for (i = 0; i < num; ++i) {
if (equal((char *) base + i * width, param)) {
return i;
}
}
return -1;
}
#define find_ele(base, num, compare, param) _find_ele(base, \
num / sizeof base[0], \
sizeof base[0], \
compare, param)
#define define_tester(name, type, type_to_find, code) \
int name(const void *_p, const void *param) { \
type *p = (type *) _p; \
type_to_find value = *(type_to_find *) param; \
return (code); \
}
struct student {
int id;
char name[20];
};
define_tester(student_tester, struct student, int,
p->id == value);
int main(void)
{
struct student student_list[] = {
0, "A",
1, "B",
2, "C"
};
int id = 2;
int index = find_ele(student_list, sizeof student_list, student_tester, &id);
if (index != -1) {
printf("find_ele(id=2) = student_list[%d]; name = %s. \n",
index, student_list[index].name);
} else {
printf("Not found. \n");
}
return 0;
}
Yes you can use void*, but while dereferencing you should know the exact type of the pointer.
So, when you can your function, add another parameter:
type = 0 for Books
= 1 for students
And then your function becomes:
int findId(void* list,int* count,unsigned int* tbid, int type)
{
Book* booklist=NULL;
Student* stulist=NULL;
int i;
if(type===0)
booklist = (Book*) list;
else if(type==1)
stulist = (Student*) list;
else
// Handle this undefined case
// And now use the same type variable to decide which pointer to use to match the values
. . . .
}
Yes you can use void pointer, if you are trying to store address of your array..Your array may contain integer types or some other datatypes stored, it doesn't matter, but right typecasting while de-referencing the void pointer is important.
I don't think you can use void* in these functions.
If you changed your functions to one and created something like:
int findObjId(void* objlist,int* count, unsigned int* objid)
{
int i;
for (i=0; i<*scount; i++)
{
if (objlist[i].id==*objid)
{
return i;
}
}
return NOT_FOUND;
}
you won't be able to extract the data from objlist. Neither *objlist, nor objlist[i] can be dereferenced to evaluate to an object. The compiler will definitely stop you from using any such statement.
If you have the option, switch to C++. Using templates, you can accomplish your goal without breaking a sweat.

Is there a tool that can refactor this C code correctly?

Lets say I have the following code (the array* function are what we use for resizable arrays and they operate on pointers-to-arrays that are null initialized):
typedef struct MyStruct
{
int i;
} MyStruct;
MyStruct* GetNewMyStruct(int i)
{
MyStruct* s = malloc(sizeof(MyStruct));
s->i = i;
return s;
}
int SomeFunction(int number, MyStruct *elem)
{
MyStruct **structs = NULL;
int i;
for (i = 0; i < number; i++)
arrayPush(&structs, GetNewMyStruct(i));
arrayPush(&structs, elem);
return arraySize(&structs);
}
I decide that SomeFunction is too large and I want refactor it. Currently where I work we use VisualAssist X, which has some refactoring capabilities, but when I use it on this it does not work correctly. If I attempt to use it to refactor out the loop, this is what I get:
void MyMethod( int number, MyStruct ** structs )
{
int i;
for (i = 0; i < number; i++)
arrayPush(&structs, GetNewMyStruct(i));
}
int SomeFunction(int number, MyStruct *elem)
{
MyStruct **structs = NULL;
MyMethod(number, structs);
arrrayPush(&structs, elem);
return arraySize(&structs);
}
This is not correct. MyMethod should take a MyStruct ***, not a MyStruct **. This is because the code I'm refactoring takes the address of structs. The result is that the refactored version will always return 1 (since only one object has been pushed into my array) rather than number+1. Are there other tools out there that do this type of refactoring correctly?
Eclipse CDT does this correctly (at least the current version Juno). Selecting the declaration of i and the loop and doing Refactor > Extract Function, and setting structs to be an output parameter, produces:
void MyMethod(int number, MyStruct*** structs) {
int i;
for (i = 0; i < number; i++)
arrayPush(&*structs, GetNewMyStruct(i));
}
int SomeFunction(int number, MyStruct *elem)
{
MyStruct **structs = NULL;
MyMethod(number, &structs);
arrayPush(&structs, elem);
return arraySize(&structs);
}

pointers/structures

I would like to ask question regarding pointers. I dont know what to do here, I have a method in the main that is calling a method outside the main, and I need to use pointers.Basically this is the rough draft of it: Thanks!
char *book[] = { "x", "y", "z",};
int number[] = { 1, 2, 3};
struct data{ char *bookname; int booknumber;};
struct data *list[3];
my_method(char *x, int y, int *z)
{
//creating a new struct
list[(*z)++] = (struct data*) malloc( sizeof(struct data) );
//assigning arguments
list[(*z)++]->bookname = x;
list[(*z)++]->booknumber = y;
(*z)++;
}
int main()
{
int nextValue = 0;
my_method(book[nextValue], book[nextValue], &nextValue);
int i;
for(i = 0; i < 3; i++)
{
function(book[i], number[i]);
printf("name: %c number: %d", list[i]->bookname, list[i]->booknumber);
}
}
It looks like you are passing the wrong arguments to your method. Try changing the following line:
my_method(book[nextValue], number[nextValue], &nextValue);
In addition, in your method you seem to be incrementing z four times, which I doubt is the behavior you want. You should only increment it once at the end, like the following:
//creating a new struct
list[*z] = (struct data*) malloc( sizeof(struct data) );
//assigning arguments
list[*z]->bookname = x;
list[*z]->booknumber = y;
(*z)++;
Try the following code. Simply removed unnecessary static void.
//declaration of method outside the main
void my_method(............,int *nextValue)
{
//................................//
//..............................//
//then increment the pointer
(*nextValue)++;
}//my_method
int main()
{
int nextValue = 0;
//Removed static void
my_method(............, &nextValue);
//........................................//
}//main

Resources