Is this a generic function pointer and is it dangerous? - c

Learning and messing up with function pointers, I noticed a way to initialize void function pointers and cast them. Yet, although I don‘t receive any warning or error, either with GCC or VS’s compiler, I wanted to know whether it was dangerous or a bad practice to do this as I don't see this way of initializing function pointers often on the Internet. Moreover, do we call this generic function pointer?
#include <stdio.h>
#include <stdint.h>
#include <conio.h>
#define PAUSE (_getch())
uint16_t add(const uint16_t x, const uint16_t y) {
return x + y;
}
char chr(uint8_t test) {
return (char)test;
}
int main(void) {
void(*test)() = (void*)add;
const uint16_t x = 1, y = 1;
uint16_t value = ((uint16_t(*)())test)(x, y);
test = (void*)chr;
printf("%d\n", add(x, y)); // 2
printf("%d\n", value); // 2
printf("%c\n", ((char(*)())test)(100)); // d
PAUSE;
return 0;
}

Is this a generic function pointer
No, if I'm not terribly mistaken, there's no such thing as a "generic function pointer" in C.
and is it dangerous?
Yes, it is. It is evil.
There are a couple of things you need to know. First, unless you are running a system that conforms to POSIX,
void(*test)() = (void*)add;
is wrong. void * is a pointer-to-object type, and as such, it is not compatible with function pointers. (At least not in standard C -- as I mentioned, POSIX requires it to be compatible with function pointers too.)
The second thing is that void (*fp)() and void (*fp)(void) are different. The former declaration permits fp to take any number of parameters of any type, and the number of arguments and their types will be inferred when the compiler sees the first call to the function (pointer).
Another important aspect is that function pointers are guaranteed to be convertible across each other (AFAIK this manifests in them having the same representation and alignment requirements). This means that any function pointer can be assigned to (the address of) any function (after an appropriate cast), so long as you do not call a function through a pointer to an incompatible type. The behavior is well-defined if and only if you cast the pointer back to the original type before calling it.
So, if you want a "generic" function pointer, you can just write something like
typedef void (*fn_ptr)(void);
and then you could assign any pointer to function to an object of type fn_ptr. What you have to pay attention to is, again, the conversion to the right type when invoking the function, as in:
int add(int a, int b);
fn_ptr fp = (fn_ptr)add; // legal
fp(); // WRONG!
int x = ((int (*)(int, int))fp)(1, 2); // good

There are two serious problems here:
A cast from a function pointer to an object pointer (such as void *) triggers undefined behavior: in principle, it could crash your system (though in practice there are many systems where it will work fine). Instead of void *, it's better to use a function-pointer type for this purpose.
You're tricking the compiler into unknowingly passing an int to a function expecting a uint8_t. That's also undefined behavior, and it's very dangerous. Since the compiler doesn't know that it's doing this, it can't even take the most basic necessary steps to avoid smashing the stack — you're really gambling here. Similarly, this is a bit more subtle, but you're also tricking the compiler into passing two int-s into a function expecting two uint16_t-s.
And two lesser problems:
The notation for function pointer types on their own — e.g., in a cast — is confusing. I think it's better to use a typedef: typedef void (*any_func_ptr)(); any_func_ptr foo = (any_func_ptr)(bar).
It's undefined behavior to call a function pointer with a different signature than the actual function has. You can avoid that with careful coding — more careful than your current code — but it's tricky and risky.

You may corrupt the call stack with this, depending on the calling convention, specifically who's doing the cleanup: http://en.wikipedia.org/wiki/X86_calling_conventions With the callee cleanup, the compiler has no way of knowing how many variables you have passed on the stack at the point of cleanup, so passing the wrong number of parameters or parameters of the wrong size will end corrupting the call stack.
On x64, everyone uses the caller cleanup, so you're safe in this regard. The parameter values, however, will in general be a mess. In your example, on x64, they will be whatever was in the corresponding registers at the time.

C11 §6.3.2.3 (8) says:
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.
And §6.7.6.3 (15) says about compatible types of functions:
[…] 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. […]
So, if you had add and chr to take int arguments (an int has at least a width of 16 bit) that would be OK (if you didn't cast the function pointer to void *), but, as it is, it is UB.

Related

Is it safe to call a function after casting it to a void return type?

I have a function that returns a value. Is it safe to cast it to a function pointer type that returns nothing (void) and then call it? Does the C standard give enough guarantees to make this safe on any platform, and equivalent to calling the function normally while ignoring the return value?
Bonus question: Is there a standards compliant calling convention that is used in practice on some platform and with which this is not safe to do?
Example code:
#include <stdio.h>
// simple function with a a side-effect;
// it is useful to call even when ignoring the return value
int f(int x) {
return printf("%d\n", x);
}
typedef void (*fun_t)(int);
int main() {
fun_t g = &f; // cast to void return type, issues a warning
g(123); // <-- is this safe, and equivalent to calling f(123) instead?
return 0;
}
Notes:
This question is concerned only with changing the return type during the cast, not changing the types of any function parameters.
I know that this is safe on the x86 and x64 platforms I use, assuming the default calling convention, but I would like to know if it is safe in general.
I know that it is easy to work around this when necessary by writing a wrapper void fw(int x) { f(x); }. However, I would still like to know if doing this is safe so that I can better understand the fundamentals of the C language.
No. This call invokes Undefined Behavior. See https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p8
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.
At link https://port70.net/~nsz/c/c11/n1570.html#6.7.6.3p15 one can find rules of compatibility of function types:
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. ...
No.
In addition to tstanisl's answer:
Consider a big return value as for example this structure:
struct big_t {
long la[1000000];
};
It is allowed as a return type.
Some compilers, including GCC for x86/x64, provide the space for the return value at the caller's site and call the function with a pointer to this space. In your use case, this pointer is not set.
So it is not safe on x86 or x64, nor any other architecture.
Have a lot of fun debugging the crashes!

Is it safe to cast a function pointer to another function pointer in C?

Is it safe to cast these 2 functions to callback pointer type then call them without casting back?
typedef void (*callback)(int i, ...);
void foo() {
// something.
}
void foo2(int i, int j) {
// something.
}
int main(void)
{
callback c = (callback)&foo, c2 = (callback)&foo2;
(*c)(0); (*c2)(0,1);
return 0;
}
The cast itself is safe, but you must use the correct type when calling the function. This is what the C standard 6.3.2.3 says:
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.
In your case void (*)(int i, ...) isn't compatible with either of the other functions, so this code is wildly undefined behavior. However, some compilers provide non-standard extensions for generic function pointer use with the non-prototype void foo() style. But that one in turn is obsolete C and shouldn't be used for that reason - always use void foo (void) in C and never empty parenthesis.
Is it safe to cast these 2 functions to callback pointer type then call them without casting back?
No. The types of the two functions are not compatible with type callback, in the language specification's sense of "compatible", therefore calling either of those functions via a pointer of type callback invokes undefined behavior. Overall, non-variadic function types are never compatible with variadic ones, and in practice, many implementations use different calling conventions for one type than for the other, such that there is no plausible reason even to hope that calling a function of one variety as if it were of the other variety would have the desired effect in any consistent way.
You have several alternatives, among them:
Use different callback types for different purposes, each appropriate to its intended callback interface. This way you can avoid casting the callback functions at all. This would be my recommendation. It achieves the best type safety, and you need somehow to keep track of what the actual callback type is anyway, so that you can call it correctly.
Use a union of function pointer types. Callback specifiers assign to the appropriate member of the union, and callback callers select the appropriate member.
typedef union {
int (*unary)(int i);
int (*binary)(int i, int j);
} callback;
// ...
callback cb1 = { .unary = foo };
callback cb2 = { .binary = foo2 };
cb1.unary(1);
cb2.binary(1, 2);
You might even use a tagged union -- one that additionally carries information about which member is used. That would be a bit more complicated to use, but it would give you a means to achieve additional type safety. One of the variations on this approach would be my fallback recommendation if you need a single data type with which multiple callback types can be conveyed.
Choose a single callback type that meets all your needs. One way to do that would be to give it a parameter of type void *, by which callback functions can accept any number and type of inputs by, for example, a pointer to a suitable structure type.
typedef int (*callback)(void *);
struct one_int { int i1; };
struct two_int { int i1, i2; };
int foo(void *args) {
struct one_int *one_int = args; // ...
}
int foo2(void *args) {
struct two_int *two_int = args; // ...
}
Choose any function type as callback. Cast to that type going in, and back to the original type for calls.
Specify the callback type without a prototype. In C, if a function declaration that is not part of a definition of that function does not specify a parameter type list then that means that no information is provided about the parameters (unlike in C++, where that means that the function has no parameters). That is compatible with functions requiring any specific number of arguments -- but not variadic ones -- provided that applying the default argument promotions to the parameter types yields compatible types. Type int is a fine parameter type in that regard. The main ones that would be a problem are integer types narrower than int, plus float.
typedef int (*callback)();
This would allow exactly the usage you describe for the particular function types in your example.
callback cb1 = foo;
callback cb2 = foo2;
(*cb1)(1); // or just cb1(1)
(*cb2)(1, 2); // or just cb2(1, 2)
Contrary to another answer's claim, support for this approach does not constitute an extension to any version of the C language specification published to date. Supporting it is a requirement for conformance with any of C89, C99, C11, and C17. However, it has been declared "obsolescent" in C17, which constitutes a warning that it may be removed from some future version of the language specification. I expect that it indeed will be removed, possibly as soon as the next version of the specification, though obsolescence does not guarantee that.
No, the pointer will still point to the 'old' function (without parameters) and if you call the function, you will put variables on the stack which will never be used.
More problematic will be, if you have a pointer to the function with parameters and cast it to the function without parameters. Then, parameters will be fetched from the stack which you never put there. It is pure chance which values the program will operate on.
Note: This is the most likely behavior, but compilers are not bound to implement it this way (due to undefined behavior).
I guess you know this, but the safer way to do this sort of thing (although it's somewhat of a nuisance) is with explicit casts:
(*(void (*)(int, int))c2)(0, 1);
Here we:
take the generic "callback" function pointer c2
cast it to the correct function pointer type void (*)(int, int)
call it with the correct arguments
More problematic is your first callback, c. You're trying to call it with one integer argument 0, but you defined foo as accepting 0 arguments. So a more correct callback to foo as defined would be
(*(void (*)(void))c)();
Or if foo was supposed to take one argument of type int, that would be
(*(void (*)(int))c)(0);
And although as I said, the extra casts here can be a nuisance, this exercise illustrates their benefit: I didn't notice the mismatch between foo's definition and your callback c, until the compiler warned me about it after I inserted what I thought were the correct casts.
Without the casts, as other answers have explained, the code is unlikely to work. In particular, the attempt to make the callback type "variadic" (that is, with the ... in the prototype) does not help, and may very well hurt. These days, the calling conventions for variadic functions tend to be different from those for ordinary functions (a distinction which the ANSI/ISO C Standard has made since the beginning, although this marked a departure from K&R C). So if the compiler thinks that c2 points to a function of type void (*)(int, ...), and you call it with (0, 2), the compiler may very well use a different calling convention (the "varargs" one), and it might not be compatible with the non-varargs foo2, which is actually of type void (*)(int, int).
The bottom line is that it is safe to cast (convert) between function pointer types, but you must do it "on both ends"; that is, you must convert back to the correct type before calling. And "the correct type" must match both the actual function being called, and the actual arguments being passed.

C cast void pointer to function pointer

I am trying to create some macros that I can use to create my own unit testing library. My header file looks like this:
#ifndef _TEST_H_
#define _TEST_H_
#include <stdio.h>
#include "hehe_stack.h"
static hehe_stack* tests;
typedef int (*testfunc)();
#define test_init() tests = hehe_stack_init();
#define test_register(test) hehe_stack_push(tests, test);
#define test_info() fprintf(stdout, "running %s :: %s \n", __FILE__, __func__);
#define test_run() testfunc = (int (*)()) hehe_stack_pop(tests); testfunc(); return 0;
#endif
In each test .c file I want to push a number of function pointers into the tests stack and then pop each function pointer out of the stack and call it. My stack pop method returns a void pointer, and the function pointer that I am pushing onto it returns an int and takes no parameters. Is my syntax incorrect? I feel like I should be able to do this.
The C99 standard does not allow to convert between pointers to data (in the standard, “objects or incomplete types” e.g. char* or void*) and pointers to functions.
6.3.2.3:8 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 pointed-to
type, the behavior is undefined.
One reason is that pointers to objects and pointers to functions do not have to be the same size. On an example architecture, the former can be 64-bit and the latter 32-bit.
You can cast from a pointer to a certain function type to a pointer to another function type, and this is the technique I recommend you use if you need to store function pointers in a data structure. Any function type will do. This means you cannot reuse a data structure intended for data pointers. You need to duplicate the data structure and change it to hold function pointers.
Do not forget to cast back to the proper function pointer type before calling, otherwise this is undefined behavior.
NOTE that, as pointed out by Andrew Mellinger, some compilers allow the conversion in each direction. C11's annex “J.5 Common extensions” lists:
J.5.7 Function pointer casts
1 A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).
2 A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4).
Some POSIX interfaces, such as dlsym(), also in effect mandate these conversions to be valid in the POSIX system's C compiler.
You can do it, but you're not supposed to do it by accident, so the syntax is made especially awkward. One does not cast the function pointer, but a pointer to a function pointer, then assigns that.
#define test_run() *((void**)&testfunc) = hehe_stack_pop(tests); testfunc(); return 0;
This turns &testfunc into a pointer to a void*, then dereferences it and assigns the value of another void* to it, which is legal.
The suggested code never compiles as you are not supposed to dereference a void * pointer (how could you? There is no type information about the particular pointer.)
The way cmotley suggests in his comment is the correct way to do this although I would recommend a little improvement for the sake of readability:
typedef int (*tTestFuncSignature)(void)
#define test_run() tTestFuncSignature testfunc = hehe_stack_pop(tests); testfunc();
or even to avoid hidden name clashes using this macro:
#define test_run() ((tTestFuncSignature)hehe_stack_pop(tests))();
Either way you have to make sure (e.g. by contract) you only got valid pointers in your stack or have to test the pointer first before invoking the function.
Edit: Corrected code formatting

How do I quiet the C compiler about a function pointer takes any number of arguments?

I have a function pointer inside a struct that gets dynamically set at runtime to the address of another function in various places in my code. It is defined in my header file like this:
void *(*run)();
During compile time, I get the following warning about this:
warning: function declaration isn't a prototype
This warning is benign, because the pointer is used in many places in my code to call the function it points to, and everything works just fine. However, I would really like to silence the warning.
If I change it to this:
void *(*run)(void);
I get compile errors whever I use it, because the various functions that make use of the pointer have different numbers of arguments, and saying void inside the parenthesies tells the compiler it accepts no arguments.
I can't use a va_list or anything fancy like that, as this is simply a pointer to another function, and I use a single pointer for them all because it keeps the code clean and simple.
I can silence the warning with adding this to my compiler flags:
-Wno-strict-prototypes
But I'd rather not have to disable compiler warnings with flags if I can avoid it.
So my question is: How do I notate this function pointer in the code in such a way that the compiler is satisfied with the fact that it accepts any number of any kind of arguments?
The code works perfectly. I just want the warning to go away.
Store the pointer as a void * and cast to the appropriate function pointer type when necessary? Keep in mind that it isn't necessarily safe to call one type of function pointer as if it were another type, so the warning you're starting out with isn't entirely invalid.
You can cast a function pointer like so:
void *genericPointer = ...;
void (*fp)(int, int) = genericPointer;
fp(123, 456);
Note that:
There's no explicit casting necessary here, as void * can always be cast to any pointer type.
The initial "void" before (*fp) is the return type of the function pointer.
You are trying to do things clean - i.e. involve the compiler in checks, but the design you invented simply cannot be clean by its principle. You cannot involve compiler in prototype checks this way, because you always must know, which parameters to pass at this particular case in runtime. Compiler cannot check this and if you make a mistake, segmentation fault is on the way.
But if I remember well, something like this was maybe used also in linux kernel (?). The solution is to have a general pointer (like the one you have) and each time you call a particular function you just typecast it to the pointer to function with the particular arguments. You may need to typecast it to void * first to silence the compiler again :-)
In C, when you call a function without a prototype visible, default argument promotions are applied to all of the arguments that you pass to the function. This means that the types that you actually pass do not necessarily match the types received by the function.
E.g.
void (*g)();
void f()
{
float x = 0.5;
g(x); // double passed
}
This means that you need to know that the function that you are actually calling has a compatible signature to that implied by the arguments that you are passing after promotion.
Given that you need to know this in any case you must know the function signature of the actual function being called at the call site which is using the function pointer. With this knowledge it is usually simpler and cleaner to use a function pointer with the correct prototype and you can avoid default argument promotion entirely.
Note that as you are defining your functions with prototypes, when you assigned a pointer to your function to a function pointer without a prototype you effective converted, say, a void(*)(int, int) to a void(*)() so it is completely correct and desirable to perform the reverse conversion before calling the function. gcc allows both these conversions without emitting any warnings.
E.g.
void PerformCall( void(*p)() )
{
if (some_condition)
{
// due to extra knowledge I now know p takes two int arguments
// so use a function pointer with the correct prototype.
void(*prototyped_p)(int, int) = p;
prototyped_p( 3, 4 );
}
}
Try typedefing the function pointer declaration and then have the caller explicityly cast it:
typedef void *(*run)();
//when calling...
void my_foo() {}
run r = (run)my_foo;
If the different function signatures are known, use a union. Otherwise, use a pointer of type void (*)(void) (actually, any function pointer type would do) to hold the generic pointer and convert to the proper type when setting the value and calling the code.
Example using a union:
union run_fn
{
void *(*as_unary)(int);
void *(*as_binary)(int, int);
};
struct foo
{
union run_fn run;
};
void *bar(int, int);
struct foo foo;
foo.run.as_binary = bar;
void *baz = foo.run.as_binary(42, -1);
Example using explicit casts:
struct foo
{
void (*run)(void);
};
void *bar(int, int);
struct foo foo;
foo.run = (void *(*)(int, int))bar;
void *baz = ((void *(*)(int, int))foo.run)(42, -1);
Don't use a void * to hold function pointers - such a conversion is unspecified by the ISO C standard and may be unavailable on certain architectures.
Ignoring the warning and using your code as-is is actually also a possibility, but keep in mind that any function argument will be subject to the default argument promotions and it's your responsibility that the promoted arguments properly match the declared parameters.

Casting a function pointer to another type

Let's say I have a function that accepts a void (*)(void*) function pointer for use as a callback:
void do_stuff(void (*callback_fp)(void*), void* callback_arg);
Now, if I have a function like this:
void my_callback_function(struct my_struct* arg);
Can I do this safely?
do_stuff((void (*)(void*)) &my_callback_function, NULL);
I've looked at this question and I've looked at some C standards which say you can cast to 'compatible function pointers', but I cannot find a definition of what 'compatible function pointer' means.
As far as the C standard is concerned, if you cast a function pointer to a function pointer of a different type and then call that, it is undefined behavior. See Annex J.2 (informative):
The behavior is undefined in the following circumstances:
A pointer is used to call a function whose type is not compatible with the pointed-to
type (6.3.2.3).
Section 6.3.2.3, paragraph 8 reads:
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 pointed-to type,
the behavior is undefined.
So in other words, you can cast a function pointer to a different function pointer type, cast it back again, and call it, and things will work.
The definition of compatible is somewhat complicated. It can be found in section 6.7.5.3, paragraph 15:
For two function types to be compatible, both shall specify compatible return types127.
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.)
127) If both function types are ‘‘old style’’, parameter types are not compared.
The rules for determining whether two types are compatible are described in section 6.2.7, and I won't quote them here since they're rather lengthy, but you can read them on the draft of the C99 standard (PDF).
The relevant rule here is in section 6.7.5.1, paragraph 2:
For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.
Hence, since a void* is not compatible with a struct my_struct*, a function pointer of type void (*)(void*) is not compatible with a function pointer of type void (*)(struct my_struct*), so this casting of function pointers is technically undefined behavior.
In practice, though, you can safely get away with casting function pointers in some cases. In the x86 calling convention, arguments are pushed on the stack, and all pointers are the same size (4 bytes in x86 or 8 bytes in x86_64). Calling a function pointer boils down to pushing the arguments on the stack and doing an indirect jump to the function pointer target, and there's obviously no notion of types at the machine code level.
Things you definitely can't do:
Cast between function pointers of different calling conventions. You will mess up the stack and at best, crash, at worst, succeed silently with a huge gaping security hole. In Windows programming, you often pass function pointers around. Win32 expects all callback functions to use the stdcall calling convention (which the macros CALLBACK, PASCAL, and WINAPI all expand to). If you pass a function pointer that uses the standard C calling convention (cdecl), badness will result.
In C++, cast between class member function pointers and regular function pointers. This often trips up C++ newbies. Class member functions have a hidden this parameter, and if you cast a member function to a regular function, there's no this object to use, and again, much badness will result.
Another bad idea that might sometimes work but is also undefined behavior:
Casting between function pointers and regular pointers (e.g. casting a void (*)(void) to a void*). Function pointers aren't necessarily the same size as regular pointers, since on some architectures they might contain extra contextual information. This will probably work ok on x86, but remember that it's undefined behavior.
I asked about this exact same issue regarding some code in GLib recently. (GLib is a core library for the GNOME project and written in C.) I was told the entire slots'n'signals framework depends upon it.
Throughout the code, there are numerous instances of casting from type (1) to (2):
typedef int (*CompareFunc) (const void *a,
const void *b)
typedef int (*CompareDataFunc) (const void *b,
const void *b,
void *user_data)
It is common to chain-thru with calls like this:
int stuff_equal (GStuff *a,
GStuff *b,
CompareFunc compare_func)
{
return stuff_equal_with_data(a, b, (CompareDataFunc) compare_func, NULL);
}
int stuff_equal_with_data (GStuff *a,
GStuff *b,
CompareDataFunc compare_func,
void *user_data)
{
int result;
/* do some work here */
result = compare_func (data1, data2, user_data);
return result;
}
See for yourself here in g_array_sort(): http://git.gnome.org/browse/glib/tree/glib/garray.c
The answers above are detailed and likely correct -- if you sit on the standards committee. Adam and Johannes deserve credit for their well-researched responses. However, out in the wild, you will find this code works just fine. Controversial? Yes. Consider this: GLib compiles/works/tests on a large number of platforms (Linux/Solaris/Windows/OS X) with a wide variety of compilers/linkers/kernel loaders (GCC/CLang/MSVC). Standards be damned, I guess.
I spent some time thinking about these answers. Here is my conclusion:
If you are writing a callback library, this might be OK. Caveat emptor -- use at your own risk.
Else, don't do it.
Thinking deeper after writing this response, I would not be surprised if the code for C compilers uses this same trick. And since (most/all?) modern C compilers are bootstrapped, this would imply the trick is safe.
A more important question to research: Can someone find a platform/compiler/linker/loader where this trick does not work? Major brownie points for that one. I bet there are some embedded processors/systems that don't like it. However, for desktop computing (and probably mobile/tablet), this trick probably still works.
The point really isn't whether you can. The trivial solution is
void my_callback_function(struct my_struct* arg);
void my_callback_helper(void* pv)
{
my_callback_function((struct my_struct*)pv);
}
do_stuff(&my_callback_helper);
A good compiler will only generate code for my_callback_helper if it's really needed, in which case you'd be glad it did.
You have a compatible function type if the return type and parameter types are compatible - basically (it's more complicated in reality :)). Compatibility is the same as "same type" just more lax to allow to have different types but still have some form of saying "these types are almost the same". In C89, for example, two structs were compatible if they were otherwise identical but just their name was different. C99 seem to have changed that. Quoting from the c rationale document (highly recommended reading, btw!):
Structure, union, or enumeration type declarations in two different translation units do not formally declare the same type, even if the text of these declarations come from the same include file, since the translation units are themselves disjoint. The Standard thus specifies additional compatibility rules for such types, so that if two such declarations are sufficiently similar they are compatible.
That said - yeah strictly this is undefined behavior, because your do_stuff function or someone else will call your function with a function pointer having void* as parameter, but your function has an incompatible parameter. But nevertheless, i expect all compilers to compile and run it without moaning. But you can do cleaner by having another function taking a void* (and registering that as callback function) which will just call your actual function then.
As C code compiles to instruction which do not care at all about pointer types, it's quite fine to use the code you mention. You'd run into problems when you'd run do_stuff with your callback function and pointer to something else then my_struct structure as argument.
I hope I can make it clearer by showing what would not work:
int my_number = 14;
do_stuff((void (*)(void*)) &my_callback_function, &my_number);
// my_callback_function will try to access int as struct my_struct
// and go nuts
or...
void another_callback_function(struct my_struct* arg, int arg2) { something }
do_stuff((void (*)(void*)) &another_callback_function, NULL);
// another_callback_function will look for non-existing second argument
// on the stack and go nuts
Basically, you can cast pointers to whatever you like, as long as the data continue to make sense at run-time.
Well, unless I understood the question wrong, you can just cast a function pointer this way.
void print_data(void *data)
{
// ...
}
((void (*)(char *)) &print_data)("hello");
A cleaner way would be to create a function typedef.
typedef void(*t_print_str)(char *);
((t_print_str) &print_data)("hello");
If you think about the way function calls work in C/C++, they push certain items on the stack, jump to the new code location, execute, then pop the stack on return. If your function pointers describe functions with the same return type and the same number/size of arguments, you should be okay.
Thus, I think you should be able to do so safely.
Void pointers are compatible with other types of pointer. It's the backbone of how malloc and the mem functions (memcpy, memcmp) work. Typically, in C (Rather than C++) NULL is a macro defined as ((void *)0).
Look at 6.3.2.3 (Item 1) in C99:
A pointer to void may be converted to or from a pointer to any incomplete or object type

Resources