Search code examples
ccastingnull-pointer

Storing and using type information in C


I'm coming from Java and I'm trying to implement a doubly linked list in C as an exercise. I wanted to do something like the Java generics where I would pass a pointer type to the list initialization and this pointer type would be use to cast the list void pointer but I'm not sure if this is possible?

What I'm looking for is something that can be stored in a list struct and used to cast *data to the correct type from a node. I was thinking of using a double pointer but then I'd need to declare that as a void pointer and I'd have the same problem.

typedef struct node {
    void *data;
    struct node *next;
    struct node *previous;
} node;

typedef struct list {
    node *head;
    node *tail;
   //??? is there any way to store the data type of *data? 
} list;

Solution

  • Typically, the use of specific functions like the following are used.

    void    List_Put_int(list *L, int *i);
    void    List_Put_double(list *L, double *d);
    int *   List_Get_int(list *L);
    double *List_Get_double(list *L);
    

    A not so easy for learner approach uses _Generic. C11 offers _Generic which allows for code, at compile time, to be steered as desired based on type.

    The below offers basic code to save/fetch to 3 types of pointers. The macros would need expansion for each new types. _Generic does not allow 2 types listed that may be the same like unsigned * and size_t *. So there are are limitations.

    The type_id(X) macros creates an enumeration for the 3 types which may be use to check for run-time problems as with LIST_POP(L, &d); below.

    typedef struct node {
      void *data;
      int type;
    } node;
    
    typedef struct list {
      node *head;
      node *tail;
    } list;
    
    node node_var;
    void List_Push(list *l, void *p, int type) {
      // tbd code - simplistic use of global for illustration only
      node_var.data = p;
      node_var.type = type;
    }
    
    void *List_Pop(list *l, int type) {
      // tbd code
      assert(node_var.type == type);
      return node_var.data;
    }
    
    #define cast(X,ptr) _Generic((X), \
      double *: (double *) (ptr), \
      unsigned *: (unsigned *) (ptr), \
      int *: (int *) (ptr) \
      )
    
    #define type_id(X) _Generic((X), \
      double *: 1, \
      unsigned *: 2, \
      int *: 3 \
      )
    
    #define LIST_PUSH(L, data)  { List_Push((L),(data), type_id(data)); }
    #define LIST_POP(L, dataptr) (*(dataptr)=cast(*dataptr, List_Pop((L), type_id(*dataptr))) )
    

    Usage example and output

    int main() {
      list *L = 0; // tbd initialization
      int i = 42;
      printf("%p %d\n", (void*) &i, i);
      LIST_PUSH(L, &i);
      int *j;
      LIST_POP(L, &j);
      printf("%p %d\n", (void*) j, *j);
      double *d;
      LIST_POP(L, &d);
    }
    
    42
    42
    assertion error