Related
I would like to know how I would go about passing any function to a function, as in a generic function pointer that can take any function whatsoever, The goal of this is to make a destructor system, so basically storing the function and calling it with it's arguments also stored later down the line,
Something like:
Defer(SDL_DestroyWindow, Window);
I already handled the arguments, but I don't know how to manage the function pointer part of this, Thank you!
Edit: I added more info ...
typedef struct {
void** args;
} IM_Defer_Resource;
/* Defer & Whatnot */
IM_Stack* IM_Defer_Stack;
void IM_Defer_Init() {
IM_Defer_Stack = IM_Stack_Init();
}
void IM_Defer(/* What to put here? */) {
}
void IM_Defer_All() {
while(IM_Defer_Stack->size) {
IM_Defer_Resource* resource = IM_Stack_Pop(IM_Defer_Stack);
if(!resource) continue;
/* What to do */
}
}
I don't have the actual functions of defer, but I did copy every argument into the stack and can pop them successfully, I don't know how to implement the variadic function calling though
Edit2:
After receiving some input: I think this would be more feasible:
Defer(SDL_DestroyWindow, "SDL_Window*", window);
I am brainstorming how this would be possible, but I would appreciate some input
Edit3:
/* Defer & Whatnot */
typedef struct {
char** types;
void** args;
int count;
} IM_Defer_Resource;
IM_Stack* IM_Defer_Stack;
void IM_Defer_Init() {
IM_Defer_Stack = IM_Stack_Init(IM_Get_Stack_Type(IM_Defer_Resource));
}
void IM_Defer_Internal(void* var, int n, ...) {
char* type;
void* arg;
va_list args;
va_start(args, n);
IM_Defer_Resource resource;
int count = n / 2;
resource->types = calloc(count, sizeof(char*));
resource->args = calloc(count, sizeof(void*));
resource->count = count;
for(count > 0; n -= 1) {
type = va_arg(args, char*);
resource->types[count-1] = type;
arg = va_arg(args, void*);
resource->args[count-1] = arg;
}
IM_Stack_Push(IM_Defer_Stack, &resource);
}
void IM_Defer_All() {
while(IM_Defer_Stack->size) {
IM_Defer_Resource* resource = IM_Stack_Pop(IM_Defer_Stack);
if(!resource) continue;
/* I have a char* and a void* to the resource, Now what? */
free(resource->types);
free(resource->args);
}
}
This is what I came up with, but I am wondering how I can conver that char* into a type...
As I said in comment a big problem is that when declaring a variadic function the undeclared parameters are subject to the default argument promotions. This means that you can find the passed arguments different from that intended by the function, that will eventually lead to exceptions. What you want to do is feasible, but really very complex.
One solution, but limited because requires a lot of coding, could be:
#include <stdarg.h>
#include <stdio.h>
typedef enum { fn1, fn2, fn3, /*....*/} e_fn;
void multi_fun(e_fn fn, ...)
{
va_list ap;
int j;
va_start(ap, fn); /* Requires the last fixed parameter (to get the address) */
switch(fn)
{
case fn1:
{
//suppose prototype for fn1 to be void fn1_fn(int, float, struct mystruct *);
int this_int = va_arg(ap, int);
float this_float = va_arg(ap, float);
struct mystruct *this_struct = va_arg(ap, struct mystruct *);
fn1_fn(this_int, this_float, this_struct);
break;
}
case fn2:
{
...
}
}
va_end(ap);
}
You should take a look at Fake Function Framework (fff) on GitHub. They've done this using macros for caching mock functions. MIT Licensed. However, just like #Frankie_C said, this requires a LOT of code. The header file that defines all of the macros is around 6K LOC. And functions are still limited to 20 arguments.
I wanna call multiple functions and deal with their return values (using pthread_join) in main(), but they are all int functions with multiple non-void arguments, and the definition of pthread_create is:
int pthread_create(pthread_t * thread,
const pthread_attr_t * attr,
void * (*start_routine)(void *),
void *arg);
all examples of the start_routine I found in the internet are type of void * with type of single void * argument, is it possible to call int functions with multiple non-void type arguments in pthread_create?
You want to wrap the int function into a function of the required type.
So assuming you want to return an int you might do it like this:
(The example assume C99 and leaves out relevant error checking for the sake of readebility.)
#include <inttypes.h> /* for intptr_t */
#include <stdio.h>
#include <pthread.h>
struct S
{
int x;
int y;
};
int sum(int x, int y)
{
return x + y;
}
void * thread_function(void * pv)
{
struct S * ps = pv;
pthread_exit((void *) (intptr_t) sum(ps->x, ps->y));
}
int main(void)
{
struct S s = {41, 1};
pthread_t pt;
pthread_create(&pt, NULL, thread_function, &s);
void * pv;
pthread_join(pt, &pv);
int z = (intptr_t) pv;
printf("%d + %d = %d\n", s.x, s.y, z);
}
This prints:
41 + 1 = 42
The casting to and from intptr_t is necessary to assure the misuse of a pointer value as an integer does not violate the C-Standard.
If you look at the manual page you will see that the function argument is
void *(*start_routine) (void *).
You cannot pass a different type of function to start routine.
You can use (void *) to pass an argument to start_routine.
You can cast the pThread pointer to some type compatible with integer. Better solution is to put the entire functionality in a wrapper function. Please refer to the below link:
C++, create a pthread for a function with a return type?
Let's see if I understood the question.
You want to call a function with a signature like
int myfunct(int a, int b, int c)
then define a struct like this
struct my_funct_param_t
{
int a ;
int b ;
int c ;
} ;
and a wrapper to use as start routine
void *myfunct1(void *arg)
{
my_funct_param_t *arg1 = (my_funct_param_t *)arg ;
myfunct(arg1->a, arg1->b, arg1->c) ;
....
}
The code that starts the thread must create the my_funct_patam_t object and fill in accordingly. Beware of this object lifetime....
I'm trying to create a system call handler, and I'm not sure how to store it.
I'm using the following typedef to store a (void *) pointer, which should receive the address of the function and an integer arg_no representing the number of arguments. Then, I create an array of this type.
typedef struct
{
void *foo;
int arg_no;
}td_sys_call_handler;
td_sys_call_handler ish[SYSCALL_HANDLER_NUM];
I'm trying to initialize the array in the following manner.
ish[0].foo = void (*halt) (void); ish[0].arg_no = 0;
ish[1].foo = void (*exit) (int status) NO_RETURN; ish[1].arg_no = 1;
ish[2].foo = pid_t (*exec) (const char *file); ish[2].arg_no = 1;
ish[3].foo = int (*wait) (pid_t); ish[3].arg_no = 1;
ish[4].foo = bool (*create) (const char *file, unsigned initial_size);
ish[4].arg_no = 2;
ish[5].foo = bool (*remove) (const char *file); ish[5].arg_no = 1;
ish[6].foo = int (*open) (const char *file); ish[6].arg_no = 1;
ish[7].foo = int (*filesize) (int fd); ish[7].arg_no = 1;
ish[8].foo = int (*read) (int fd, void *buffer, unsigned length);
ish[8].arg_no = 3;
ish[9].foo = int (*write) (int fd, const void *buffer, unsigned length);
ish[9].arg_no = 3;
ish[10].foo = void (*seek) (int fd, unsigned position);
ish[10].arg_no = 2;
ish[11].foo = unsigned (*tell) (int fd); ish[11].arg_no = 1;
But all the assignments from the function pointer to the void pointer produce the following error:
../../userprog/syscall.c: In function ‘syscall_init’:
../../userprog/syscall.c:76:17: error: expected expression before ‘void’
../../userprog/syscall.c:77:17: error: expected expression before ‘void’
../../userprog/syscall.c:78:17: error: expected expression before ‘pid_t’
../../userprog/syscall.c:79:17: error: expected expression before ‘int’
../../userprog/syscall.c:80:17: error: expected expression before ‘_Bool’
../../userprog/syscall.c:82:17: error: expected expression before ‘_Bool’
../../userprog/syscall.c:83:17: error: expected expression before ‘int’
../../userprog/syscall.c:84:17: error: expected expression before ‘int’
../../userprog/syscall.c:85:17: error: expected expression before ‘int’
../../userprog/syscall.c:87:17: error: expected expression before ‘int’
../../userprog/syscall.c:89:17: error: expected expression before ‘void’
../../userprog/syscall.c:91:17: error: expected expression before ‘unsigned’
I was under the impression that void* is the only instance of polymorphism in the language and that it can point to anything.
However, it appears that I'm wrong.
So which is the type of the pointer which can store the address of any function type?
Also, can you give me a good reference about C polymorphism? I've looked in many books but as far as I've seen the polymorphism chapter is very thin.
Thank you.
Yes, you are wrong.
void * pointers can point at any kind of data, but in C code (functions) are not data.
It's not valid to even cast between void * and function pointers: even though on most contemporary computers it will work as expected, the language does not guarantee that.
I don't understand from your code how you intended the "overloading" to be used in practice, how do you expect to call through the foo pointer? Just having the expected number of arguments is not enough, arguments have types and thus are handled differently in the function call.
The notation you need casts the system call function pointer to void *:
ish[0].foo = (void *)halt;
The C standard does not guarantee that pointers to functions will fit into pointers to data such as void *; fortunately for you, POSIX steps in and does guarantee that pointers to functions are the same size as pointers to data.
Your syntax is wrong. You should declare your function pointer first. Then you can use the address of the function pointer to assign to the pointer.
void (*halt) (void) = halt_sys_call_function;
ish[0].foo = &halt; ish[0].arg_no = 0;
C doesn't support traditional inheritance relationships in a direct way, but it does guarantee that the address of a structure is also the address of the first member of the structure. This can be used to emulate polymorphism in C. I described a similar approach in an answer I wrote about dynamic dispatch in C.
Consider a struct formatted to hold each function specifically:
typedef struct
{
void (*halt) (void);
void (*exit) (int status);
pid_t (*exec) (const char *file);
int (*wait) (pid_t);
bool (*create) (const char *file, unsigned initial_size);
bool (*remove) (const char *file);
int (*open) (const char *file);
int (*filesize) (int fd);
int (*read) (int fd, void *buffer, unsigned length);
int (*write) (int fd, const void *buffer, unsigned length);
void (*seek) (int fd, unsigned position);
unsigned (*tell) (int fd);
} myFuncs;
OR
This is messy and VERY unmaintable, but if you did cast each pointer to a void*, using void *addressOfWait = (void*)&wait;, then you could re-cast to the correct function pointer type before calling:
int (*waitFunctionPointer)(pid_t) = addressOfWait;
Then you could call that pointer:
waitFunctionPointer((pid_t) 1111); //wait for process with pid of 1111
I'll ask for #problemPotato's forgiveness for filching his structure definition:
typedef struct
{
void (*halt) (void);
void (*exit) (int status);
pid_t (*exec) (const char *file);
int (*wait) (pid_t);
bool (*create) (const char *file, unsigned initial_size);
bool (*remove) (const char *file);
int (*open) (const char *file);
int (*filesize) (int fd);
int (*read) (int fd, void *buffer, unsigned length);
int (*write) (int fd, const void *buffer, unsigned length);
void (*seek) (int fd, unsigned position);
unsigned (*tell) (int fd);
} fs_ops;
Say you have matching functions, declared like:
int ext5_open(const char * file);
unsigned ext5_tell (int fd);
then you can define and initialize a variable like (the bare name of the function is a pointer to it):
fs_ops ext5_ops = {
.open = ext5_open,
.tell = ext5_tell,
};
Fields that aren't initialized get NULL (i.e., pointer to no function). You can change the value of a field, ask if it is set (if(ext5_ops.seek == NULL) ...), and call the function:
retval = ext5_ops.(*ext5_open)("/tmp/junk");
(the parenteses around (*ext5_open) are because * (pointer indirection) binds less strongly than function call).
A function pointer can be converted into a void *, but it's a little trickier to convert it back to correct function-pointer type in order to call it. It should be possible by using a union. You'll need a separate union-member of the correct type for type of function that you want to store. And, as user4815162342 notes in a comment, you'll need to manage all the various combinations, probably with an enum.
typedef struct
{
union {
void *vp;
void (*v__v)(void);
void (*v__i)(int);
pid_t (*pid__ccp)(const char *);
int (*i__pid)(pid_t);
bool (*b__ccp_u)(const char *, unsigned);
bool (*b__ccp)(const char *);
int (*i__ccp)(const char *);
int (*i__i)(int);
int (*i__i_vp_u)(int, void *, unsigned);
int (*i__i_cvp_u)(int, const void *, unsigned);
void (*v__i_u)(int, unsigned);
unsigned (*u__i)(int);
} fp;
int arg_no;
}td_sys_call_handler;
The idea here is to try to encode the types into the identifiers, as a kind of "apps-Hungarian". This way, the meaning of any of these identifiers is directly visible.
It may be easier to generate these pointers and the associated enum at the same time. I think the easiest way to manage this part is with my favorite trick, X-Macros. Warning: it just gets more and more weird.
#define function_types(_) \
_(v__v, void, void) \
_(v__i, void, int) \
_(pid_ccp, pid_t, const char *) \
_(i__pid, int, pid_t) \
_(b__ccp_u, const char *, unsigned) \
_(b__ccp, const char *) \
_(i__ccp, const char *) \
_(i__i, int) \
_(i__i_vp_u, int, void *, unsigned) \
_(i__i_cvp_u, int, const void *, unsigned) \
_(v__i_u, int, unsigned) \
_(u__i, unsigned, int) \
/* end function_types */
This "master"-macro is a comma separated table of tokens which is passed, row by row, to the _ underscore macro, which is passed-in.
Now the struct type can be constructed by writing additional macros to use the rows, these are passed-in as _ to the table macro to instantiate the template:
#define create_function_pointer(id, ret, ...) \
ret (*id)(__VA_ARGS__);
#define create_function_type_id(id, ret, ...) \
f__ ## id
typedef struct {
union {
void *vp;
function_types(create_function_pointer)
} fp;
int arg_no;
enum {
function_types(create_function_type_id)
} type;
} td_sys_call_handler;
Now an array of these structs can be populated:
td_sys_call_handler ish[SYSCALL_HANDLER_NUM];
int i=0;
ish[i++] = (td_sys_call_handler){ halt, 0, f__v__v };
ish[i++] = (td_sys_call_handler){ exit, 1, f__v__i };
ish[i++] = (td_sys_call_handler){ exec, 1, f__pid__ccp };
ish[i++] = (td_sys_call_handler){ wait, 1, f__i__pid };
ish[i++] = (td_sys_call_handler){ create, 2, f__b__ccp_u };
ish[i++] = (td_sys_call_handler){ remove, 1, f__b__ccp };
ish[i++] = (td_sys_call_handler){ open, 1, f__i__ccp };
ish[i++] = (td_sys_call_handler){ filesize, 1, f__i__i };
ish[i++] = (td_sys_call_handler){ read, 3, f__i__i_vp_u };
ish[i++] = (td_sys_call_handler){ write, 3, f__i__i_cvp_u };
ish[i++] = (td_sys_call_handler){ seek, 2, f__v__i_u };
ish[i++] = (td_sys_call_handler){ tell, 1, f__u__i };
Now, calling a function given one of these structs will require (as you surmised) a switch, with a separate case for each signature. It needs to crack the arguments using stdarg and the call with the appropriate union member function pointer.
void make_sys_call(td_sys_call_handler ish, ...){
va_list ap;
int i;
const char *ccp;
pid_t pid;
bool b;
void *vp;
unsigned u;
const void *cvp;
va_start(ap, ish);
switch(ish.type) {
case f__v__f: ish.fp.v__v();
break;
case f__v__i: i = va_arg(int);
ish.fp.v__i(i);
break;
case f__pid__ccp: ccp = va_arg(const char *);
ish.fp.pid__ccp(ccp);
break;
// etc.
}
va_end(ap);
}
It will not be possible to return different types directly. You will either need to allocate a union type variable to hold the return value and return that, or something even more insane. An external stack data type could hold unions of the various return types. Depending on profiling results, it may be appropriate to consider this instead of returning the unions.
HTH.
I'm writing a unit test framework (see SO for more details). Or view the code at GitHub.
Safer Code describes a way to pass functions of arbitrary types.
But how do I call such a function without knowing its types beforehand? Assume f needs no input, so f() should work on its own.
Let's say I want to populate an array using an arbitrary generator function.
void* gen_array(fp gen, size_t size) {
int i, len = gen_int() % 100;
void* arr = GC_MALLOC(len * size);
for (i = 0; i < len; i++) {
arr[i] = gen(NULL);
}
return arr;
}
It should look something like this, but I get compiler errors:
gcc -o example example.c qc.c qc.h -lgc
In file included from example.c:1:
qc.h:21: error: expected declaration specifiers or ‘...’ before ‘size_t’
In file included from qc.c:1:
qc.h:21: error: expected declaration specifiers or ‘...’ before ‘size_t’
qc.c:23: error: conflicting types for ‘gen_array’
qc.h:21: error: previous declaration of ‘gen_array’ was here
qc.c: In function ‘gen_array’:
qc.c:29: warning: dereferencing ‘void *’ pointer
qc.c:29: error: too many arguments to function ‘gen’
qc.c:29: error: invalid use of void expression
qc.h:21: error: expected declaration specifiers or ‘...’ before ‘size_t’
make: *** [example] Error 1
After thinking about some more I realize your problem your above code would never work.
You are first calling trying to call a void function with no parameters with the parameter NULL. Next you would need your code to be more generic. I placed an example below of what I mean. Now using a global variable
#include <stdio.h>
#include <stdlib.h>
typedef void (*fp)(void);
void * GEN_ARRAY_TEMP;
int gen_int() {
return 67;
}
void* gen_array(fp gen, size_t size) {
int i, len = gen_int() % 100;
void* arr = malloc(len * size);
void* arr_end = arr + len * size;
GEN_ARRAY_TEMP = arr;
while (GEN_ARRAY_TEMP <= arr_end) {
gen();
GEN_ARRAY_TEMP+=size;
}
return arr;
}
void make_int() {
(*(int*)GEN_ARRAY_TEMP) = 9;
}
int main() {
int i;
int * gen_int_array = (int*) gen_array(make_int, sizeof(int));
for(i=0;i<67;i++) {
printf("%d\n",gen_int_array[i]);
}
}
That page suggests you make the function pointer take a void*. So in order for your code to compile, you must pass it a void pointer:
typedef void* (*fp)(void*);
doit(fp f) {
f(NULL);
}
And just make sure that the function that you're calling simply ignores the parameter.
Generally speaking, these generic function pointers are used for starting threads. The void pointer is simply a pointer to a struct that holds the actual parameters.
What would you need to do is wrap your function in a void function like so
#include <stdio.h>
typedef void (*fp)(void);
int sum(int x,int y) {return x+y;}
void doit(fp f) {
f();
}
void func() {
printf("Hello %d\n",sum(1,2));
}
int main() {
doit(func);
}
You have two problems:
First, qc.h is missing a <stdlib.h> include. This is needed for use of size_t.
Second, in gen_array, you create a void *arr, then try to dereference it as an array (arr[i]). Since the compiler doesn't know the size of your array elements, it cannot fill the array. You must treat it as a char *, offset by arr + size * i, and pass it into gen rather than taking a return (returns also need to know the structure size):
// ...
char *arr = GC_MALLOC(len * size);
for (int i = 0; i < len; i++) {
gen(arr + i * size, NULL);
}
return arr;
This will of course require changing the fp type definition.
For the case where your pointer to a function 'fp' is of type which takes no argument and returns void, in which case you should declare it as :
typedef void (*fp)();
In the above case the call should be :
(*gen)();
If your pointer to the function 'fp' is of type which takes 'void *' as argument and returns void, in which case you should declare it as :
typedef void (*fp)(void *);
In the above case the call should be :
(*gen)(NULL);
or any other pointer variable you might want to pass.
As far as your example goes try this :
typedef void * (*fp)(void *);
void* gen_array(fp gen, size_t size) {
int i, len = gen_int() % 100;
void* arr = GC_MALLOC(len * size);
for (i = 0; i < len; i++) {
arr[i] = (*gen)(NULL);
}
return arr;
}
The solution consists of two parts, one is a static library that receives instances of struct from the user of the library. Library doesn't know what will be the type of structs, all it knows there will be two function pointers to it with a specific name.
Library Code
pre-compiled library has no way of knowing types of user structs, hence receiving via void*
void save(void *data) {
// library will save/cache user's object
data->registered(); // if register successful
}
void remove(void *data) {
// library will remove the object from memory
data->remove(); // if removed successful
}
User of the Library Code
struct Temp { // random order of fields
void (*custom1)();
void (*registered)();
void (*custom2)();
void (*remove)();
void (*custom3)();
}
void reg() {
printf("registered");
}
void rem() {
printf("removed");
}
void custom1() {}
void custom2() {}
void custom3() {}
var temp = malloc(struct Temp, sizeof(struct Temp));
temp->registered = reg;
temp->remove = rem;
temp->custom1 = custom1; // some custom functions
temp->custom2 = custom2;
temp->custom3 = custom3;
// calling library code
save(temp);
remove(temp);
Q. Is there a way for the Library to know how to iterate and go through member fields and see if there's a pointer to such function and call it available.
Is there a way for the Library to know how to iterate and go through member fields and see if there's a pointer to such function and call it available.
No there is not.
Your best bet is to create a structure in the library that has these members, and pass that structure instead of void*.
As #immibis said, there is no way for this to work (i.e. no way for the compiler to justify compiling such code) if the compiler does not know what the types of the data being passed to the function are.
Since you wanted to pass the objects along to the library without storing information about the type of each object in the library, you can fake polymorphism in C, by doing the following:
callback.h
#ifndef _CALLBACK_H_
#define _CALLBACK_H_
typedef struct {
void (*registered)();
void (*removed)();
} ICallback;
#endif _CALLBACK_H_
pre_comp.h
#ifndef _PRE_COMP_H_
#define _PRE_COMP_H_
#include "callback.h"
void save(ICallback* data);
void remove(ICallback* data);
#endif /* _PRE_COMP_H_ */
precomp.c
#include <stdlib.h> /* NULL */
#include "callback.h"
#include "pre_comp.h"
void save(ICallback *data) {
if (NULL != data && NULL != data->registered) {
data->registered(); // if register successful
}
}
void remove(ICallback *data) {
if (NULL != data && NULL != data->removed) {
data->removed(); // if removed successful
}
}
main.c
#include <stdio.h>
#include "pre_comp.h"
#include "callback.h"
struct Temp {
ICallback base; // has to be defined first for this to work
void (*custom1)();
void (*custom2)();
void (*custom3)();
};
// calling library code
void reg() {
puts("registered");
}
void rem() {
puts("removed");
}
int main() {
struct Temp data = {{reg, rem}};
save((ICallback*)&data);
remove((ICallback*)&data);
}
compiling
gcc pre_comp.c main.c
output
registered
removed
If the library has 0 information about the possible struct types, then you
cannot do it. The library has to get somehow the information or the offsets.
The only way I can think of is:
All register member have the same prototype
Pass the offset to the function.
I created an example of this
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
// function that does not know anything about any struct
void reg(void *data, size_t offset)
{
uintptr_t *p = (uintptr_t*) (((char*) data) + offset);
void (*reg)() = (void(*)()) *p;
reg();
}
struct A {
int c;
void (*reg)();
};
struct B {
int b;
int c;
void (*reg)();
};
void reg_a()
{
printf("reg of A\n");
}
void reg_b()
{
printf("reg of B\n");
}
int main(void)
{
struct A a;
struct B b;
a.reg = reg_a;
b.reg = reg_b;
reg(&a, offsetof(struct A, reg));
reg(&b, offsetof(struct B, reg));
return 0;
}
This prints:
$ ./c
reg of A
reg of B
I run it with valgrind and I did not get any errors nor warnings. I'm not sure if
this violates somehow strict aliasing rules or yields undefined behaviour
because of the uintptr_t* conversions, but at least it seems to work.
I think however, the more cleaner solution is to rewrite the register (btw. register
is a keyword in C, you cannot use that for a function name) function to
accept a function pointer and possible parameters, something like this:
#include <stdio.h>
#include <stdarg.h>
void reg(void (*func)(va_list), int dummy, ...)
{
if(func == NULL)
return;
va_list ap;
va_start(ap, dummy);
func(ap);
va_end(ap);
}
void reg1(int a, int b)
{
printf("reg1, a=%d, b=%d\n", a, b);
}
void vreg1(va_list ap)
{
int a = va_arg(ap, int);
int b = va_arg(ap, int);
reg1(a, b);
}
void reg2(const char *text)
{
printf("reg2, %s\n", text);
}
void vreg2(va_list ap)
{
const char *text = va_arg(ap, const char*);
reg2(text);
}
int main(void)
{
reg(vreg1, 0, 3, 4);
reg(vreg2, 0, "Hello world");
return 0;
}
This has the output:
reg1, a=3, b=4
reg2, Hello world
Note that reg has a dummy parameter. I do that because the man page of
stdarg says:
man stdarg
va_start():
[...]
Because the address of this argument may be used in the va_start() macro,
it should not be declared as a register variable, or as a
function or an array type.
You can take an approach similar to qsort and pass function pointers in addition to a void pointer to the structure.
Here is the function prototype for qsort, which is a function that can be used to sort arrays of any type:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
It takes a function pointer that performs the comparison because without it qsort wouldn't know how to compare two objects.
This can be applied to your task with a function prototype like this:
int DoFoo(void *thing, void (*register)(void *), void (*remove)(void *))
This function takes a void pointer to your struct and then two functions that it can call when it needs to register or remove that struct. Having the functions be members of the struct is not required and I generally do not recommend it. I recommend reading up on qsort because it is does something similar to what you are trying to do.