Search code examples
ccastingfunction-pointersunionsc89

Would casting to a union of compatible return types satisfy the criteria of compatibility for function pointers?


To explain why I'm attempting to do this, the constraints of my environment require me to work with auto-generated code. The generated code is very similar and I would like to call a batch of functions that are basically the same. I need to use a solution that is C89 or C99 compliant.

From reading the specs the below seems to be legal code, but I'm not certain about casting a function pointer to return a union type. Can anyone point out if this is legal or what part of the spec it violates?

#include <stdio.h>
#include <stdlib.h>

/* Automagically generated types */

struct A_return_type {
    int index;
    unsigned int options;
};

struct A_return_type *A_function(int x) {
    struct A_return_type *A_return = malloc(sizeof(*A_return));
    A_return->index = x;
    A_return->options = 0xA;
    return A_return;
}

struct B_return_type {
    int index;
    unsigned int options;
};

struct B_return_type *B_function(int x) {
    struct B_return_type *B_return = malloc(sizeof(*B_return));
    B_return->index = x;
    B_return->options = 0xB;
    return B_return;
}

struct C_return_type {
    int index;
    unsigned int options;
};

struct C_return_type *C_function(int x) {
    struct C_return_type *C_return = malloc(sizeof(*C_return));
    C_return->index = x;
    C_return->options = 0xC;
    return C_return;
}

/* End generated types */

int main(int argc, char *argv[]) {
    /*--------------------------------------------------------------
    All of the generated methods take the same arguments and return
    structs with the same members in the same order. It is permitted
    to inspect the common initial part of any structs in a union,
    per C89 3.3.2.3 p5.
    --------------------------------------------------------------*/
    union return_types {
        struct {
            int index;
            unsigned int options;
        } common_return;
        struct A_return_type A_return;
        struct B_return_type B_return;
        struct C_return_type C_return;
    };

    /*----------------------------------------------------------
    Function pointers are compatible if their return types and
    parameter lists are compatible per C89 3.5.4.3 p9.
    ----------------------------------------------------------*/
    typedef union return_types *(*generated_function)(int);

    generated_function function_array[] = {
          (generated_function)A_function
        , (generated_function)B_function
        , (generated_function)C_function
    };

    for(int i = 0; i < sizeof(function_array)/sizeof(function_array[0]); ++i) {
        printf("%x\n", function_array[i](0)->common_return.options);
    }
}

Solution

  • You can convert pointers to functions to other types of pointers to functions all you want (C 2018 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…”), but if you use a converted pointer to call a function with a type that is not compatible, the C standard does not define the behavior (ibid: “… If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.”).

    A function that returns a struct A_return_type * is not compatible with a function that returns a union return_types *. You are almost saved by 6.2.5 28, which says “… All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other…”, and a footnote there says “The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and members of unions.” This interchangeability means a function returning struct foo * is compatible with a function returning struct bar *. Unfortunately, you are calling a function that returns a pointer to a structure with an expression for a function that returns a pointer to a union, and the standard does not say these have the same representation and alignment requirements or that they are interchangeable.