C run time type checking-Collection of common programming errors

I have a C library storing records with a number of fields. Schema are read in from a text file, including the type of each field in the record.

To simplify for question purposes, imagine I have

typedef enum my_type_enum
{
    INT32, //32-bit integer
    MYSTRUCT, //some struct I have, details irrelevant
    ...
} my_type_enum;

typedef struct my_var
{
    my_type_enum typetag;
    unsigned char* data;
} my_var;

my_var myrecord[numfields];

The schema file says whether each field of myrecord should hold an int32_t or a mystruct. My library reads the schema file and for each my_var in myrecord sets the tag and allocates the right amount space for the data.

my_var is opaque and client programs basically use, for simple data

void set(my_var* record, size_t field, void * src)
{
    memcpy(record[field].data, src, datatypes[record[field].typetag].size);
}

int32_t x = 5; 
set(myrecord, 0, &x);

to store a value in a record, and a similar get() to take things out.

The tagged my_var type allows type checking once data is inside the my_var, but if the schema says record holds three INT32s there is of course nothing to check that src points to an int32_t and not a mystruct when you’re trying to set() data into that my_var.

Obviously the check needs to take place in something wrapping set(), before the int32_t* or mystruct* is converted to void*. I have seen compile-time checking with typeof() trickery. I feel like what I want probably isn’t possible, but you never know all the tricks…

Is there anything I can do better than providing facilities that read a schema at client program compilation time and generate a set_CHECKED() wrapper macro that will give a compiler error if someone tries to copy an int32_t into a my_var tagged to hold mystruct? GCC extensions are fine.

  1. Actually, the ‘set’ and ‘get’ seem to be the correct places to put your checks. If accessing a field with the wrong type is a fatal error, the fix is simple:

    void set(my_var* record, size_t field, void * src)
    {
        if (record[field].typetag != (my_var)src->typetag) {
            fprintf(stderr, "Type mismatch!\n");
            exit(1);
        }
        memcpy(record[field].data, src, datatypes[record[field].typetag].size);
    }
    

    If a type mismatch is not a fatal error, you’ll need to make set and get return and error code and handle that correctly at the call site.

    Note that in C, type information is gone by the time the code is compiled. The ‘typetag’ field is the only way you have to know the type of a field.

    Of course, if you don’t need to be able to change the schema without recompiling the library, you could try generating C code from it. That would let you use the compiler’s type checking at compile time.

    (Also: not a big deal, but my_var.data should be a void*, not a char*. This doesn’t change the correctness of the code but void* tells the reader (and debugger) that the type is unknown.)

Originally posted 2013-11-09 20:01:02.