Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check Type of structure in C

Tags:

c

I made a simple listing struct in C that holds any kind of data using void* pointer, something like:

struct node
{
    void *the_data;
    node *next;
};

It is working just fine, but I have some lists that contains, for example, only struct_a in a list and only struct_b in another list. I need to check if the first element of the list is of struct_a type or struct_b type in a single if() question so I know the data that this list is holding. How to do this? Thanks

Edit:

I've come up with a simple solution that is enough for the problem now, dunno if it's the best:

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

And I added a "descriptor" node, which contains the address of the first node and info about the type:

struct list_desc
{
    node *list;
    short int type;
}

and I'll use it with some macros like

#define __data_type1 10
#define __data_type2 20

so later I can compare like if( struct.type == __data_tipe1 ) etc...

like image 867
Fnr Avatar asked Dec 19 '22 01:12

Fnr


2 Answers

C does not provide run-time type information. You have to add this information yourself. There are two basic approaches (assuming C99 or C11):

You can use a struct (potentially) with anonymous union:

struct Node {
    enum {
        OBJ_TYPE_A,
        OBJ_TYPE_B
    } type;
    union {
        struct NodeA a;
        struct NodeB b;
    };    // anonymous, if a name is added, you have an additional field name
};

...

struct Node *node = ...;

if ( node->type == OBJ_TYPE_A ) {

    node->a.a_field = whatever;

...

Cons are you have to define the union in a single place and cannot extend this in another module.


A better approach is to build a true class-hierarchy with anonymous structs:

struct Node {
    enum {
        OBJ_TYPE_A,
        OBJ_TYPE_B
    } type;
    struct Node *next;
};

struct NodeA {
    struct Node;    // base class, first field!
    int a_field;
};

struct NodeB {
    struct Node;    // base class, first field!
    float b_field;
};

void take_node(struct Node *node)
    // takes a generic pointer to the base-class
{

    if ( node->type == OBJ_TYPE_A ) {
        struct NodeA *a_node = (NodeA)node;
        a_node->a_field = whatever;
    } else if ...

    ...

}

// until here that is normal C99

int main(void)
{
    struct NodeA anode = { .type = OBJ_TYPE_A, .a_field = 42 };
    struct NodeB bnode = { .type = OBJ_TYPE_B, .b_field = 4.2 };
    take_node((Node *)&anode);    // standard: inhibits type checking
    take_node(&anode);    // plan9-extension: no cast required
    take_node(&bnode);    // plan9-extension: no cast required
}

Pro is there is no need to extend the base struct (it does not depend on the child structs) when creating new child structs. The anonymous structs allow to create children of the direct children and access fields as one would expect in OOP (no chain of field specifiers: child1_1_1_ptr->child1_1.child1.base.base_field, but just child1_1_1_ptr->base_field).

The latter two invocations of take_node uses the gcc C-extension -fplan9-extensions. This avoids to cast to the base-class while still enabling type-checking (base class or a child). This is a large step towards OOP already.

That can be extended to a full size OOP approach, including virtual methods. However, there is a point using a true OOPL like C++ becomes easier.

like image 143
too honest for this site Avatar answered Jan 06 '23 03:01

too honest for this site


C doesn't contain any runtime information about what a pointer is pointing to. You can create a struct containing a type field and a union containing whatever else you'd like to store.

enum node_type {
    NODE_TYPE_STRING,
    NODE_TYPE_INT,
    NODE_TYPE_DOUBLE,
    NODE_TYPE_OTHER_STRUCT
};

struct other_struct {
    // define some fields
};
struct node_data {
   enum node_type type;
   union {
       char *string;
       int i;
       double d;
       struct other_struct other;
    } data;
};
like image 40
dbush Avatar answered Jan 06 '23 04:01

dbush