Type-safe generic data structures in plain-old C?

后端 未结 10 2151
庸人自扰
庸人自扰 2020-12-04 08:23

I have done far more C++ programming than \"plain old C\" programming. One thing I sorely miss when programming in plain C is type-safe generic data structures, which are p

10条回答
  •  攒了一身酷
    2020-12-04 08:50

    There's a common variation to option 1 which is more efficient as it uses unions to store the values in the list nodes, ie there's no additional indirection. This has the downside that the list only accepts values of certain types and potentially wastes some memory if the types are of different sizes.

    However, it's possible to get rid of the union by using flexible array member instead if you're willing to break strict aliasing. C99 example code:

    #include 
    #include 
    #include 
    #include 
    
    struct ll_node
    {
        struct ll_node *next;
        long long data[]; // use `long long` for alignment
    };
    
    extern struct ll_node *ll_unshift(
        struct ll_node *head, size_t size, void *value);
    
    extern void *ll_get(struct ll_node *head, size_t index);
    
    #define ll_unshift_value(LIST, TYPE, ...) \
        ll_unshift((LIST), sizeof (TYPE), &(TYPE){ __VA_ARGS__ })
    
    #define ll_get_value(LIST, INDEX, TYPE) \
        (*(TYPE *)ll_get((LIST), (INDEX)))
    
    struct ll_node *ll_unshift(struct ll_node *head, size_t size, void *value)
    {
        struct ll_node *node = malloc(sizeof *node + size);
        if(!node) assert(!"PANIC");
    
        memcpy(node->data, value, size);
        node->next = head;
    
        return node;
    }
    
    void *ll_get(struct ll_node *head, size_t index)
    {
        struct ll_node *current = head;
        while(current && index--)
            current = current->next;
        return current ? current->data : NULL;
    }
    
    int main(void)
    {
        struct ll_node *head = NULL;
        head = ll_unshift_value(head, int, 1);
        head = ll_unshift_value(head, int, 2);
        head = ll_unshift_value(head, int, 3);
    
        printf("%i\n", ll_get_value(head, 0, int));
        printf("%i\n", ll_get_value(head, 1, int));
        printf("%i\n", ll_get_value(head, 2, int));
    
        return 0;
    }
    

提交回复
热议问题