5

Is there any way to create generic data structure in C and use functions in accordance with the stored data type, a structure that has various types of data and for example can be printed according to the stored data.

For example,

Suppose I wish to make a binary search tree that has just float's, int's stored. The natural approach to do would be to create an enumeration with int's and float's. it would look something like this:

Typedef enum {INT, FLOAT} DataType;

Typedef struct node
{
    void *data;
    DataType t;
    struct node *left,
                *right;
}Node;

if i want print it out:

void printTree(Node *n)
{
    if (n != NULL)
    {
        if (n->t == INT)
        {
            int *a = (int *) n->data;
            printf("%d ", *a);
        }
        else
        {
            float *a = (float *) n->data;
            printf("%f ", *a);
        }

        printTree(n->left);
        printTree(n->right);
    }
}

That's ok but i want to store another data type as a stack, query or something else. So that's why I created a tree that does not depends on a specific data type, such as:

Typedef struct node
{
    void *data;
    struct node *left,
                *right;
}Node;

If i want to print it out i use callback functions, such as:

Node *printTree(Node *n, void (*print)(const void *))
{
    if (n != NULL)
    {
        print(n->data);
        printTree(a->left);
        printTree(a->right);
    }
}

But it falls down when i try to insert a integer and a float and print it out. My question is, Is there a way of creating a generic data structure that a routine depends on a specific data type in one situation but another situation it doesn't , for mixed data type? In this situation i should create a structure that stores int's and float's stores it and use a print function like in the first print code for that in the callback function?

observation: I just declared a node in the structure and did everything on it trying to simplify, but the idea is to use the structure with .h and .c and all this abstraction involving data structures.

Peter
  • 53
  • 1
  • 1
  • 5
  • Create a union with a pointer type (`void *`), and types for your non-pointers (`int`, `float`, etc.). –  Dec 11 '13 at 23:25
  • I first thought of templates but then noticed you want for C... My bad. To answer your question, not that I'm aware of. You may create a union of multiple types for data storage. One thought is to have a structure with two member, a union for storage and one flag value for data type. – Xephon Dec 11 '13 at 23:25
  • @TimCooper Union may be problematic while processing, ie printing. How will you notify exactly what data type is in the structure? You'll need one extra flag value. – Xephon Dec 11 '13 at 23:26
  • 5
    I think the only safe way to do this is with a bunch of preprocessor stuff .. http://stackoverflow.com/questions/10950828/simulation-of-templates-in-c .. this is what C++ was invented for :) – qwwqwwq Dec 11 '13 at 23:28
  • So in essence, C doesn't support branched-actions (allowing different sets of functions) based on data type? – Minh Tran Dec 23 '16 at 22:09

3 Answers3

5

I would suggest trying something like the following. You'll noticed that Node contains a tagged union that allows for either a pointer type, an integer, or a floating point number. When Node is a pointer type, the custom print function is called, and in the other cases, the appropriate printf format is used.

typedef enum {POINTER, INT, FLOAT} DataType;

typedef struct node
{
    DataType t;
    union {
        void *pointer;
        int integer;
        float floating;
    } data;
    struct node *left,
                *right;
} Node;

void printTree(Node *n, void (*print)(const void *))
{
    if (n != NULL) {
        switch (n->t) {
            case POINTER:
                print(n->data.pointer);
                break;
            case INT:
                printf("%d ", n->data.integer);
                break;
            case FLOAT:
                printf("%f ", n->data.floating);
                break;
        }
        printTree(a->left, print);
        printTree(a->right, print);
    }
}
  • I just finished typing and saw your identical answer... Meh... +1 – Xephon Dec 11 '13 at 23:33
  • This is as good as it gets. If you coded C++, you could call a generic `node->print()` without knowing it's type, but that's a story for another day. – Guido Dec 11 '13 at 23:37
  • but that will not only solve this particular case, i mean it will only be able to print 3 data types, isn't? – Peter Dec 12 '13 at 03:23
3

C doesn't support this kind of generic data types/structures. You have a few options you can go with:

  • If you have the opportunity to use Clang as the compiler, there's a language extension to overload functions in C. But you have to cast the argument to the specific type, so the compiler knows which function to call.

  • Use C++

    • although you still have to cast the argument, so the compiler knows which of the available functions called print he has to call.

    • use templates

  • Create a function called print which takes something like

    struct data_info {
      void *data;
      enum_describing_type type;
    }
    

    print does a switch and calls the appropriate printInt, printFloat etc.

JDS
  • 1,173
  • 12
  • 26
  • Thanks. And it would extend for whatever other kind of routine that involves a specific type of data, right? – Peter Dec 12 '13 at 03:42
0

uthash is a collection of header files that provide typed hash table, linked list, etc. implementations, all using C preprocessor macros.

Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328