Compile temporary polymorphic type checks in C?

Polymorphic structures are fairly common in C, but often involve explicit casts that allow you to accidentally cast incompatible structures.

struct ID {
    char name[32];
};

struct IntID {
    struct ID id_base;
    int value;
}
struct FloatID {
    struct ID id_base;
    float value;
}

void id_name_set(ID *id, const char *name)
{
    strlcpy(id->name, name, sizeof(id->name));
}

/* macro that happens to use 'id_name_set', this is a bit contrived */
#define ID_NAME_SET_AND_VALUE(id, name, val) \
    do { \
        id_name_set((ID *)id, name); \
        id->value = val; \
    } while(0)

void func(void)
{
    struct { int value; } not_an_id;

    /* this can crash because NotID doesn't have an ID as its first member */
    ID_NAME_SET_AND_VALUE(not_an_id, "name", 10);
}

      

The problem here is that we cannot enter an argument id

in a macro against one type, since it can be id

any structure with id

as its first member.

A lot of the code I've seen just refers to structure all over the place, but it seems like it's possible to have a more robust method.

Is there a way to check at compile time?


Note that for the purposes of this question, we can assume that all structs use the same member name for the struct inherited from.


Note, I was hoping I could use something like this ...

#  define CHECK_TYPE_POLYMORPHIC(val, member, struct_name) \
    (void)(_Generic((*(val)), \
        /* base-struct */  struct_name: 0, \
        /* sub-struct */   default: (_Generic(((val)->member), struct_name: 0))))

/* --- snip --- */
/* check that `var` is an `ID`, or `var->id_base` is */
CHECK_TYPE_POLYMORPHIC(var, id_base, ID);

      

... but that doesn't work for id

types in case default

- because they don't have a member id

.

So far the only way I've found to do this is type checking with a complete list of all structures, which is not ideal in some cases (may be many or defined locally, so not known to the macro, see Compile-time check with multiple types in C? ).

+3


source to share


2 answers


You shouldn't use broadcasts. Casting assumes that you know what you are doing, and in the worst case, undefined behavior. You must rely on the types you are interested in having this field struct ID

with the same name.

Then in case you imagine where you have a functional macro do-while

, you can easily place a helper variable:

#define ID_NAME_SET_AND_VALUE(id, name, val) \
    do {                                     \
        ID* _id = &((id)->id_base));         \
        id_name_set(_id, (name));            \
        _id->value = (val);                  \
    } while(0)

      



If all goes well, this is nop, unless constraint violation and compilation aborted.

In a context where you cannot place a variable, you can use a compound literal, something like

 (ID*){ &((id)->id_base)) }

      

+1


source


The closest thing in C11 (the latest C standard) for compile-time polymorphism is type using a keyword _Generic

, but I'm not sure if it suits your needs.

The GCC component also provides you __builtin_type_compatible_p

with which you could build, for example some macros.



You can also customize GCC with MELT extensions

+1


source







All Articles