mcandre
mcandre

Reputation: 24602

How do I call an arbitrary C function passed to another function?

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

Upvotes: 2

Views: 1519

Answers (5)

Anoop Menon
Anoop Menon

Reputation: 637

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;
}

Upvotes: 0

rouzier
rouzier

Reputation: 1180

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]);
    }
}

Upvotes: 1

bdonlan
bdonlan

Reputation: 231063

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.

Upvotes: 0

Mysticial
Mysticial

Reputation: 471209

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.

Upvotes: 1

rouzier
rouzier

Reputation: 1180

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);
}

Upvotes: 0

Related Questions