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...
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.
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;
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With