简体   繁体   中英

Setting pointer type at compile time

I want to create a generic linked-list. The list need to hold only one type but that type needs to be defined by the programmer. what I want to do is to allow the programmer to call a macro SETTYPE(T) , where T will be the type of data the list will hold. In the code below I want to replace the void * in node struct with the type provided by the programmer. The code is broken but I only wanted to give the idea of what I want to achieve. Help would be appreciated.

#ifndef TYPE
#define TYPE
#define SETTYPE(T) T
typedef struct node {
    T *data;
    struct node *next;
} node;
typedef node *list;
#endif

node *new_node(void *data) {
    node *_l = malloc(sizeof *_l);
    _l->data = malloc(*data);
    memcpy(_l->data, data, sizeof(*data));
    _l->next = NULL;
    return _l;
}

you would need to use an auto generate code with "define" for the entire linked list - This means creating every method and struct within the #define so at compile time the compiler will know which type to use for example:

// type_list.h
#define define_list(T) \
typedef struct { \
    T* data; \
    struct node* next; \
} list_##T;

The above code can be called like so:

#include <stdio.h>
#include "type_list.h"

define_list(int)
define_list(float)

// This is the developer you want to help
int main(int argc, char const *argv[])
{
    list_int *a;
    list_float *b;
    return 0;
}

for more information on how to create auto generated codes look at this post:

Simulation of templates in C (for a queue data type)

hope that helped

An alternative to the solution that has already been given to you involving concatenating the type identifier to the type involved (I should also named the structure in my solution, but I'm not going to correct my colleague @AmitBerger, that has given a good solution to the problem)

This solution allows you to link nodes to several lists (not by reference) and allows you to make a list of globals (or, in general not dynamically allocated memory) or even a mixture of it.

My solution is a bit different. I wrote, based on an idea I extracted from the linux kernel, some time ago, one solution to the linked list that I'm going to copy below. The solution is just the opposite, just define a node structure, and embed (as many times as lists can the node belong to) it on the structure of your type. In this way, you can have nodes belonging to several lists, which you can only have if you finally end storing references to the actual node (if you implement the solution proposed by the other answer). (This is frequently used in data structures in the kernel, be it linux or another)

#ifndef LISTS_H
#define LISTS_H

#include <assert.h>

typedef struct LNODE_S {
        struct LNODE_S *n_prev;
        struct LNODE_S *n_next;
} LNODE_T, *LNODE_P;

typedef struct LIST_S {
    size_t  l_size;
    LNODE_P l_first;
    LNODE_P l_last;
} LLIST_T, *LLIST_P;

#ifndef OFFSETOF
        /* gives the offset of field f in type T */
#define OFFSETOF(T,f) ((char*)&(((T*)0)->f)-(char*)0)
#endif

        /* initialize fields of list L */
#define LIST_INIT(L) do{                  \
        (L)->l_size=0;                    \
        (L)->l_first=(L)->l_last=0;       \
    }while(0)

/* list declaration (with initializer) */
#define LIST_DECLARE(L)                   \
    LLIST_T L={                           \
        .l_size=0,                        \
        .l_first=0,                       \
        .l_last=0\
    }

/* get first, next, prev or last nodes of a list */
#define LIST_FIRST(L) ((L)->l_first)
#define LIST_LAST(L) ((L)->l_last)
#define LIST_NEXT(N) ((N)->n_next)
#define LIST_PREV(N) ((N)->n_prev)

/* get the list node correspondent to list tagged F of struct element passed as E */
#define LIST_NODE(E,F)      ((LNODE_P)&(E)->F)
/* get the corresponding struct element to a list node N (embedded as field F) */
#define LIST_ELEMENT(N,T,F) ((N)?(T*)((char*)(N)-OFFSETOF(T,F)):0)

/* first element of list L (tagged as L field in T) of type T */
#define LIST_FIRST_ELEMENT(L,T,F) (LIST_ELEMENT(LIST_FIRST(L),T,F))
#define LIST_LAST_ELEMENT(L,T,F) (LIST_ELEMENT(LIST_LAST(L),T,F))
#define LIST_NEXT_ELEMENT(E,T,F) (LIST_ELEMENT(LIST_NEXT(LIST_NODE(E,F)),T,F))
#define LIST_PREV_ELEMENT(E,T,f) (LIST_ELEMENT(LIST_PREV(LIST_NODE(E,F)),T,F))

/* for(;;) loop, you add the body after the macro call. Nodes */
#define LIST_FOREACH(p, L) for(p=LIST_FIRST(L);p;p=LIST_NEXT(p))
#define LIST_FORBACK(p, L) for(p=LIST_LAST(L);p;p=LIST_PREV(p))
/* idem, but you get the element of type T reference */
#define LIST_FOREACH_ELEMENT(p,L,T,F) for(p=LIST_FIRST_ELEMENT(L,T,F);p;p=LIST_NEXT_ELEMENT(p,T,F))
#define LIST_FORBACK_ELEMENT(p,L,T,F) for(p=LIST_LAST_ELEMENT(L,T,F);p;p=LIST_PREV_ELEMENT(p,T,F))

/* check for emptyness */
#define LIST_EMPTY(L) (!(L)->l_size)

/* list insertion/append */
#define __LIST_COMMON_INSERT(L,N,fst,lst,nxt,prv) do{   \
        (N)->nxt = (L)->fst;                            \
        (N)->prv = 0;                                   \
        if ((L)->fst)                                   \
            (L)->fst->prv=(N);                          \
        else                                            \
            (L)->lst=(N);                               \
        (L)->fst=(N);                                   \
        (L)->l_size++;                                  \
    }while(0)
#define LIST_INSERT(L,N) __LIST_COMMON_INSERT(L,N,l_first,l_last,n_next,n_prev)
#define LIST_APPEND(L,N) __LIST_COMMON_INSERT(L,N,l_last,l_first,n_prev,n_next)

/* node unlink */
#define __LIST_COMMON_UNLINK(L,N,lst,nxt,prv)           \
        if ((N)->nxt) (N)->nxt->prv = (N)->prv;         \
        else {                                          \
            if((L)->lst != (N)) break;                  \
            (L)->lst = (N)->prv;                        \
        }

#define LIST_UNLINK(L,N) do{                            \
        if (!(L)->l_size) break;                        \
        __LIST_COMMON_UNLINK(L,N,l_last,n_next,n_prev); \
        __LIST_COMMON_UNLINK(L,N,l_first,n_prev,n_next);\
        (L)->l_size--;                                  \
        (N)->n_next = (N)->n_prev = 0;                  \
    } while(0)

/* same for elements, you provide the list reference, Element reference, type of elements, (F)ield name of the list nodes in T */
#define LIST_INSERT_ELEMENT(L,E,T,F) LIST_INSERT(L,LIST_NODE(E,F))
#define LIST_APPEND_ELEMENT(L,E,T,F) LIST_APPEND(L,LIST_NODE(E,F))
#define LIST_UNLINK_ELEMENT(L,E,T,F) LIST_UNLINK(L,LIST_NODE(E,F))

#endif /* LISTS_H */

Usage of this module (just a header file) is shown below:

/* Standard include files */
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include "lists.h"

/* type of elements we are double linking. */
struct nodo {
        int     num;
        LNODE_T node; /* list nodes, just two pointers */
};

LIST_DECLARE(lista_nodos); /* declaration and initialization */

/* adding a node */
struct nodo *add(int num) {
    struct nodo *n = malloc(sizeof *n); /* get a node, you don't need to malloc, it can be a global. */
    n->num = num;
    LIST_APPEND_ELEMENT(&lista_nodos, n, struct nodo, node);
    return n;
}

void list(){
    struct nodo *n;
    printf("size=%d: ", lista_nodos.l_size);

    /* foreach loop, n is the iterator, and you must specify the type and the field in the type that has the pointers. */
    LIST_FOREACH_ELEMENT(n, &lista_nodos, struct nodo, node) {
        printf("<%d>", n->num);
    }
    printf(";\n");
}

int main (int argc, char **argv)
{
    struct nodo *n;
    add(1); list();
    add(2); list();
    add(3); list();

    while(n = LIST_FIRST_ELEMENT(&lista_nodos, struct nodo, node)) {
        LIST_UNLINK_ELEMENT(&lista_nodos, n, struct nodo, node);
        list();
    }

    add(4); 
    list();

    n = add(5); 
    list();

    add(6); 
    list();

    LIST_UNLINK_ELEMENT(&lista_nodos, n, struct nodo, node); list();

    while(n = LIST_LAST_ELEMENT(&lista_nodos, struct nodo, node)) {
        LIST_UNLINK_ELEMENT(&lista_nodos, n, struct nodo, node); 
        list();
    }
} /* main */

If you want to download this code, it is stored in my repository in github . You are free to download and use it, it is copyrighted by me, and usable under BSD license.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM