I created 2 functions, to read and write to a path, declared as such:
int Read(const char * /*Filename*/, void * /*Ptr*/, size_t /*Size*/), Write(const char * /*Filename*/, const void * /*Ptr*/, size_t /*Size*/);
I created an additional function to that will call one of the above functions with a path
static int IOData(int(*const Func)(const char *, void *, size_t)) {
char Filename[DATA_PATH_LEN];
// Build path
return Func(Filename, &Data, sizeof(Data));
}
However, when Write
is passed as a callback to IOData
, the compiler raises the following warning
Incompatible pointer types passing 'int (const char *, const void , int)' to parameter of type 'int ()(const char *, void *, int)'
Would casting a function that accepts a const pointer to a function that accepts a non const pointer be undefined behavior?
I noticed that there is an almost identical question but that question uses C++ but this question uses plain C so using templates is not an option
This is not allowed because the types of one of the corresponding parameters is not compatible.
Compatible types are defined in section 6.2.7p1 of the C standard:
Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers, and in 6.7.6 for declarators. ...
And section 6.7.3p10 details compatibility of qualified types:
For two qualified types to be compatible, both shall have the identically qualified version of a compatible type; the order of type qualifiers within a list of specifiers or qualifiers does not affect the specified type.
This means that const void *
and void *
are not compatible.
Compatibility of function types is described in section 6.7.6.3p15:
For two function types to be compatible, both shall specify compatible return types. Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types. If one type has a parameter type list and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifier list, the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions. If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier. (In the determination of type compatibility and of a composite type, each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.)
So because one set of corresponding parameters are not compatible, the function types are not compatible.
Finally, section 6.5.2.2p9 regarding the function call operator ()
describes what happens in this case:
If the function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.
So calling a function through an incompatible function pointer type triggers undefined behavior and therefore should not be done.