Search code examples
cpointersfunction-pointerstypedefunions

Is it a bad idea to create a generic "function pointer" union in C?


For a project I'm working on, it's desirable to have a generic "pointer to a function" type. However, in C, to have a pointer to a function, you need to specify the prototype in the type of the function pointer.

For example, if I have the function void setdata(short data), I can't store that in the same pointer as I would a function int getdata(), because their arguments and return values are different.

After some lateral thinking, I came up with the following workaround:

typedef long (* PFL)(); /* pointer to function that returns a long... */
typedef short (* PFI)(); /* pointer to function that returns a short... */
typedef void (* PFV)(); /* pointer to function that returns a void... */
typedef void (* PFVAL)(long); /* pointer to function that returns a void...
                                but has a long argument...*/
typedef void (* PFVAI)(short); /* pointer to function that returns a void...
                                but has an short argument...*/
typedef void (* PFVAU)(unsigned short);
typedef void (* PFVAII)(short, short); /* same as above, but two shorts... */
typedef void (* PFVAUU)(unsigned short, unsigned short); /* same as above, but two shorts... */

typedef union {
    PFV         pfv;
    PFI         pfi;
    PFL         pfl;
    PFVAL       pfval;
    PFVAI       pfvai;
    PFVAU       pfvau;
    PFVAII      pfvaii;
    PFVAUU      pfvauu;
} FP;

Sure enough, I'm able to initialize instances of this type like so:

FP funcpointer = { .pfvai = setdata };

Clang and GCC don't complain. Is this a bad idea?


Solution

  • What you're doing is valid. As long as you're calling the pointed-to function via the proper pointer type, it's well defined.

    This union could get big however depending on how many different function types you have to support, and you have to keep it in sync with your set of typedefs. As it turns out you can freely convert from one function pointer type to another via a cast, you just need to make sure you call it with the proper type.

    Section 6.3.2.3p8 of the C standard say the following about function pointer conversions:

    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.

    So you could also just use void (*)() as a generic pointer type instead of using a union, then you would need to apply the proper cast when you call it. For example:

    typedef void (*FP)();
    FP fp = setdata;
    ...
    ((PFVAI)fp)(123);