0

Is it safe to convert between voidarg and chararg or between voidarg and fooarg?:

typedef int (*voidarg)(void *);
typedef int (*chararg)(char *);
typedef int (*fooarg)(foo_t *);

Or between voidret and charret or between voidret and fooret?:

typedef void *(*voidret)(int);
typedef char *(*charret)(int);
typedef foo_t *(*fooret)(int);

I know this may not be safe in C++ (particularly with an unknown foo_t type), but is it safe according to any of the C standards or major C compilers?

EDIT:

I forgot to mention an important point. I want to call the functions too, i.e. to be able to do this:

int charfunc(char *s) { /* ... */ }
char str[] = "...";

int somefunction(voidarg f, void *data)
{
    return f(data);
}

int main()
{
    int i = somefunction(charfunc, str);
}
Matt
  • 21,026
  • 18
  • 63
  • 115
  • 1
    possible duplicate of [Casting a function pointer to another type](http://stackoverflow.com/questions/559581/casting-a-function-pointer-to-another-type) – Barmar Jan 27 '15 at 17:04

2 Answers2

1

I would expect the cast to be safe, iff the pointer is cast to the correct type before calling. This also assumes that they are all declared with the same calling-convention decorations.

I've done this here in my operator constructor function which stores various function types in a "generic" form, but they are cast to the correct type in operator execute function. This appears to work with gcc in Cygwin/mingw/Ubuntu, suncc with solaris, and Visual Studio. My alternative here was unprototyped function pointers which gave me loads of noisy compiler warnings.


For the update, No, that doesn't look safe to me at least from a portability point of view. Calling the the function through a pointer of the wrong type is undefined behavior. The void * type is capable of storing a pointer to any object type (not necessarily a function, but that's not the issue here). And a function pointer is capable of storing a function pointer of a different signature, but calling through the wrong type may achieve something like a "reinterpret_cast" or it may blow up.

luser droog
  • 18,988
  • 3
  • 53
  • 105
  • I have updated the question, in case that gives you more information for your answer. – Matt Jan 27 '15 at 17:48
  • But `reinterpret_cast`ing shouldn't be an issue in C (as opposed to C++) because there are no multiple inheritance issues to deal with that can change pointer values. – Matt Jan 27 '15 at 20:43
  • That's the wrong term, I'm sorry. It must have some further significance in C++ that I wasn't aware of. I meant that you're assuming that a `void *` and `char *` have the same representation, which is possible, even likely, but not guaranteed by the standard, and so should be avoided. It may be better to use a macro to make a template for the function and instantiate one for each type. If they truly are represented the same, then the compiler should optimize it for you, but if they are not the same, it will still work when ported to some new architecture where the representations differ. – luser droog Jan 29 '15 at 07:19
  • I think casting from a `void *` to `char *` to access the bytes is still *implementation-defined* (unless the type was originally `char *` or `unsigned char *` or equivalent, like `uint8_t`), but sneaking the cast through a function-pointer is ... well, I'm too lazy to look it up, but that's the part that gives me a twinge in the gut. *Lie to the compiler, and it will have its revenge!* – luser droog Jan 29 '15 at 07:24
  • You didn't use the wrong term. `reinterpret_cast` (which itself only exists in C++) just means looking at the same bits as if they were another type. This can be different from a regular pointer cast when dealing with C++ classes that use multiple inheritance, because in such casts, the pointer might actually need to be moved by an offset. C doesn't have such a problem, so casting never needs to move a pointer. And as far as I know, in C, all non-function pointers have the same representation (and usually even function pointers as well, but I'm not dealing with that right now). – Matt Jan 29 '15 at 16:39
0

I guess you answered your own question: no it is not safe. If you are casting voidarg (both from and to) you are actually saying to the compiler you know what you are doing.

This is a flexibility the compilers give you, but you have to pay the price of being responsible for the correct casting.

hsgubert
  • 2,266
  • 1
  • 22
  • 23