I am confused about using C function pointers.
using fn_p1 = void(int); // function pointer
using fn_p2 = void (*)(int);
void functional(fn_p1 f) {
f(1);
}
void callback(int value){
// do something
}
int main() {
fn_p2 f = callback; //works
fn_p1 f1 = static_cast<fn_p1>(f); //does not work
fn_p1 f2 = callback; //does not work
fn_p1 f2 = static_cast<fn_p1>(callback); //does not work
functional(f); // works, the argument is form of void(*)(int)
f(1); // works
functional(*f); // works, the argument is dereferenced, void(int)
(*f)(1); // works
return 0;
}
I know there is no difference if you call a function pointer with f(1)
, (*f)(1)
, or (*****f)(1)
.
I don't get how functional(f);
works but fn_p1 f1 = static_cast<fn_p1>(f);
and its variants can not since they define the function pointer as using fn_p1 = void(int);
.
Could anyone explain how the function pointer works or how the compiler deals with it?
CodePudding user response:
The most useful error that the IDE should give you is on the line fn_p1 f2 = callback;
:
Illegal initializer (only variables can be initialized) [illegal_initializer]
(This is the message I get from clangd.)
That means literally that an entity of type void(int)
(or more in general someReturnType(someArgTypes...)
) is not a variable.
Indeed, in C functions are not first class, which means that you can't pass them to function and don't get them in return from function; when you think you are successfully doing so, you're in reality passing or taking back function pointers.
In other words there's no such a thing in C as "a function value". Functions are not values that you can assign to.
When you write
fn_p1 f2 = callback; // assuming using fn_p1 = void(int);
you are truly trying to create a variable of type void(int)
. But such a thing doesn't exist, hence the error.
The static_cast
s don't work for fundamentally the same reason.
As regards
void functional(fn_p1 f) {
f(1);
}
function-to-(function)pointer decaying is happening. f
is truly of type fn_p1*
.
You can easily verify it by writing an invalid statement in functional
in order to trigger a compiler error telling you what the type of f
is, like this
void functional(fn_p1 f) {
int foo = f;
f(1);
}
Clangd answers this
Cannot initialize a variable of type 'int' with an lvalue of type 'fn_p1 ' (aka 'void ()(int)') [init_conversion_failed]
which indirectly tells you that f
is of type fn_p1*
, so you better write that instead of fn_p1
as the type of the parameter of functional
, at least for clarity (similarly to how you should prefer writing T*
instead of T[]
as a parameter type in a function taking a c-style array of T
s).
If you truly want to assign functions to variables, you should look at lambdas, struct
s operator()
, std::function
, and the topic of function objects in general.