What you're doing is partially OK, but the bit that isn't OK is probably the most important bit.
In C, void *
is a generic pointer for any object type (C11 6.3.2.3.1):
A pointer to void
may be converted to or from a pointer to any
object type.
It is undefined behavior to convert a pointer to a function to a void *
. A function is not an object.
It is OK to convert a function to a function of one type to a pointer to a function of another type, and back again, without loss of information (C11 6.3.2.3.8). In your case, you are converting from, in one case, an int (*)(int)
to a void *(*)()
. The (void *(*)())
cast would be OK, and the (void *)
case would not be OK.
Where you run into trouble is when you try to actually call that function from the converted type. Again, from C11 6.3.2.3.8:
If a converted pointer is used to call a function whose type is not
compatible with the referenced type, the behavior is undefined.
So in your case, when you convert myfun2()
, for instance, to a.function
with a.function =(void *(*)())myfun2
, the conversion itself is fine. But what you cannot do is then call that function via a.function('a')
, because the types of the two functions are not compatible, so you're into undefined behavior.
Since you define A.function
as a pointer to a function with an unspecified number of arguments, you may be OK for compatibility on the parameter front per C11 6.7.6.3.15:
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
and since there is only one parameter of type int
in myfun2()
, this is satisfied. What will really mess you up is the return type, since void *
and int
are incompatible, so you have undefined behavior. In reality, on most modern 64 bit systems, void *
and int
will not be the same size, so you could expect unpredictable results trying to do this on such a system even if it will let you.
Because the conversion itself is OK, you can use any function pointer to store any other, so if you wanted to you could store additional information about the function type in the struct, and use it later to convert to a compatible type, for instance:
if ( a.return_type == RET_TYPE_INT && a.arg_type == ARG_TYPE_INT ) {
int (*fp)(int) = (int(*)(int)) a.function;
int n = fp('a');
/* Do something with n */
}
else if ( a.return_type = RET_TYPE_CHAR && a.arg_type == ARG_TYPE_INT ) {
char (*fp)(int) = (char(*)(int)) a.function;
char ch = fp('a');
/* Do something with ch */
}
and so on, where RET_TYPE_CHAR
and ARG_TYPE_INT
are constants you've defined yourself, and return_type
and arg_type
are additional members of struct A
that you'd populate when setting a.function
.