Passing function pointer as an argument using va_args - c

I am trying to pass a function pointer as part of a number of arguments under va_arg. I tried using a void * wildcard, before typecasting it later, but that gives an error.
fn_ptr = va_arg(*app, (void*));
How does one pass a function pointer, as an argument to another function using va_args?

Just pass the type of the function pointer to va_arg. For example:
#include <stdio.h>
#include <stdarg.h>
void foo()
{
printf("foo\n");
}
typedef void(*foo_ptr)();
void bar(unsigned count, ...)
{
va_list args;
va_start(args, count);
unsigned i = 0;
for (; i < count; ++i) {
foo_ptr p = va_arg(args, foo_ptr);
(*p)();
}
va_end(args);
}
int main()
{
bar(2, &foo, &foo);
}
Live demo

Related

In C language, is there any way to use callback with arbitrary / variable arguments?

I would like to send callbacks with different signatures for the same function. Somenthing like this:
#include <stdio.h>
#include <stdarg.h>
void a(int pa) {}
void b(int pb1, float pb2) {}
// exec implementation
int main() {
exec(a, 1);
exec(b, 1, 2.3);
}
I thought of using something like:
void exec(void (*func)(...), ...) {
int arg1;
float arg2;
va_list valist;
va_start(valist, size);
arg1 = va_arg(valist, int);
if (size == 1) {
(*func)(arg1);
va_end(valist);
return;
}
arg2 = va_arg(valist, float);
if (size == 2) {
(*func)(arg1, arg2);
va_end(valist);
return;
}
}
But obviously it doesn't work :(
The usual solution to making callback function interfaces flexible with respect to data provided to the function is to give the callback signature a void * parameter (possibly in addition to other parameters). Arbitrary data can be provided via such a parameter. Something like this:
void exec(void (*func)(void *), void *data) {
func(data);
}
struct s2 {
int i;
float f;
};
void func1(void *data) {
int i = *(int *)data;
// ...
}
void func2(void *data) {
struct s2 s = *(struct s2 *)data;
// ...
}
int main(void) {
int i = 42;
struct s2 s = { .i = 17, .f = 3.14 };
exec(func1, &i);
exec(func2, &s);
}
HOWEVER, It is possible to do something more like you describe, where the callback functions genuinely have different signatures, by specifying the callback type without a prototype. In that case, there are still at least these caveats:
If the callback functions themselves are defined with prototypes (as they should be) then the parameter types should not be any that are altered by the default argument promotions. So, pointers, ints, doubles, but not floats or short ints or chars (not an exhaustive list). If you wanted to support other parameter types then you would need to cast the function pointer before calling the function, as described later.
The callback functions cannot be variadic.
If the front-end is variadic, then it needs to be told at runtime, somehow, what the actual number and types of the arguments are.
Furthermore, there will need to be explicit calls to the callback functions, with correct arguments, so there can be only a fixed set of predetermined callback signatures supported.
For example, that might look something like this:
enum sig { INT, INT_DOUB };
void exec(void (*func)(/* no prototype */), enum sig cb_sig, ...);
void a(int pa) {}
void b(int pb1, double pb2) {}
int main(void) {
exec(a, INT, 1);
exec(b, INT_DOUB, 1, 2.3);
}
void exec(void (*func)(/* no prototype */), enum sig cb_sig, ...) {
va_list valist;
va_start(valist, cb_sig);
switch (cb_sig) {
case INT: {
int i = va_arg(valist, int);
func(i);
break;
}
case INT_DOUB: {
int i = va_arg(valist, int);
double d = va_arg(valist, double);
func(i, d);
break;
}
default:
assert(("Can't be reached", 0));
}
va_end(valist);
}
It is possible that that would elicit a few warnings, such as about a function declaration that does not provide a prototype, and about calling a (declared, but) unprototyped function. Since you know the signatures by the time you execute the calls, however, you could get rid of the latter kind of warning via appropriate casting. For example,
// ...
case INT: {
int i = va_arg(valist, int);
((void (*)(int))func)(i);
break;
}
// ...
You could change the callbacks to take a single va_list argument:
void a(va_list args)
{
int pa = va_arg(args,int);
}
void b(va_list args)
{
int pb1 = va_arg(args,int);
double pb2 = va_arg(args,double);
}
And have your other function pass the va_list along.
void exec(void (*func)(va_list), ...)
{
va_list valist;
va_start(valist, func);
func(valist);
va_end(valist);
}
You can use va_args to solve this.
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#define exec_func(func, ...) func(__VA_ARGS__)
long func(char *a, int b, long c, long d)
{
printf("a: %s, b: %d, c: %ld, d: %ld\n", a, b, c, d);
return c + d;
}
int main()
{
printf("c + d: %ld\n", exec_func(func, "test", 10, 1000, 1000));
}

Passing a pointer by reference crashes the programme

I'm trying to create a class with memory allocated during an object initialisation (using init procedure). The programme crashes with segfault at runtime. As I have found out, this is caused because I pass the pointer by reference **sequence. Even if I allocate memory before procedure call, the segfault takes place. And once I pass the pointer as a pure value *sequence the fault is gone. Cannot I pass pointers by reference?
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
typedef struct
{
int *sequence;
} set;
void init(int **sequence, unsigned amount, ...)
{
va_list args;
va_start(args, amount);
*sequence = malloc(amount * sizeof(**sequence));
for (unsigned i = 0; i < amount; i++)
*sequence[i] = va_arg(args, int);
va_end(args);
}
int main(void)
{
set x;
init(&x.sequence, 5, 0, 1, 2, 3, 4);
for (char i = 0; i < 5; i++)
printf("%u\t", x.sequence[i]);
}
Change this:
*sequence[i] = va_arg(args, int);
to this:
(*sequence)[i] = va_arg(args, int);
The [] operator binds more tightly than *, so parentheses are needed to obtain the desired grouping.

C: Passing any function to a function

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.

What does "..." mean in a C function declaration?

What does this mean?
void message(int x, int y, ...)
I can't understand what ... is.
Can anybody explain?
... denotes a variable list of arguments that can be accessed through va_arg, va_end and va_start.
Unspecified/variable number of parameters. To handle such function you have to use the va_list type and va_start, va_arg, and va_end functions:
An example taken from here:
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
int maxof(int, ...) ;
void f(void);
main(){
f();
exit(EXIT SUCCESS);
}
int maxof(int n args, ...){
register int i;
int max, a;
va_list ap;
va_start(ap, n args);
max = va_arg(ap, int);
for(i = 2; i <= n_args; i++) {
if((a = va_arg(ap, int)) > max)
max = a;
}
va_end(ap);
return max;
}
void f(void) {
int i = 5;
int j[256];
j[42] = 24;
printf("%d\n",maxof(3, i, j[42], 0));
}
You can find more details here
You have defined a function message somewhere that takes at least two arguments of type int and then some optional arguments indicated by the "...". (printf is another function taking optional arguments).
The optional arguments can be accessed using the va_* functions.
... represents final argument passed as an array or as a sequence of arguments.
It's the variable argument formal parameter. From the syntactical prospective it allows you pass a variable number of parameters (at least two, which are x and y, but even more).

How to pass a function pointer to a function with variable arguments?

I don't know how to accomplish this!
how to get the function pointer in va_list arguments?
thanks so much.
Typedefs often make working with function pointers easier, but are not necessary.
#include <stdarg.h>
void foo(int count, ...) {
va_list ap;
int i;
va_start(ap, count);
for (i = 0; i < count; i++) {
void (*bar)() = va_arg(ap, void (*)());
(*bar)();
}
va_end(ap);
}
Use a typedef for the function pointer type.

Resources