Passing foo(int *) as argument to foo(void*) in X

Viewed 111

A have a functions:

void somefunc(void func(void *)) { ... }

void foo(int *num) {...} 

I'm trying to pass foo as an argument to somefunc but receive a warning. What is the correct way to do this?

2 Answers

Having a parameter that is a function with one or more void * parameters is a common way of implementing some type-generic algorithms. (This answer assumes that to be the case in this code. Other possibilities exist but are unlikely.) The parameter func to somefunc provides a callback function that somefunc uses to operate on the data without knowing its type.

Note that while the parameter is declared as a function, void func(void *), it is automatically adjusted to be a pointer to a function, void (*func)(void *). Removing the name, we have the abstract type, void (*)(void *).

You cannot pass a void (*)(int *) argument for a void (*)(void *) parameter, because they are incompatible types. And you should not attempt to coerce the foo argument into the parameter type with a cast, as the behavior of calling the void (int *) function with that converted pointer is not defined by the C standard. A proper solution is to pass a pointer to a function of the expected type. You can make a new function for this:

void FooWrapper(void *p) { foo(p); }

Then you can pass FooWrapper to somefunc. When somefunc calls FooWrapper, it will pass it a void *. When FooWrapper calls foo, that void * will be automatically converted to an int *. (Presumably, foo is declared beforehand, with a prototype.) As long as the void * that FooWrapper passes originated as a pointer to some int, this conversion from void * back to int * is defined by the C standard, so this is proper code.

Alternatively, instead of creating a wrapper, you can modify foo to take a void * parameter and convert it to int * internally:

void foo(void *VoidNum)
{
    int *num = VoidNum;
    …
}

Then you may pass foo directly to somefunc.

I'm trying to pass foo as an argument to somefunc but receive a warning.

The error occurs because somefunc is declared to expect an argument of type void (*)(void *), but the argument foo being passed has the type void (*)(int *). The two types are different and incompatible.

What is the correct way to do this?

Function pointers can be cast between different types, so the following will compile without error.

void bar()  { somefunc((void (*)(int *))foo); }

Using an auxiliary typedef makes the same code more readable.

typedef void (*VOID_FUNC)(void *);

void bar()  { somefunc((VOID_FUNC)foo); }

The important caveat is that the only legal/safe use of the func argument inside somefunc is to cast it back to the real type of the original function before using it. From the relevant paragraph 6.3.2.3/8 in the language standard: "A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined."

Below is an abbreviated example of correct usage.

extern int n;
void foo(int *num);

typedef void (*VOID_FUNC)(void *);
typedef void (*INT_FUNC)(int *);

void somefunc(VOID_FUNC func)  { ((INT_FUNC)func)(&n); }

void bar()  { somefunc((VOID_FUNC)foo); }
Related