get ready for a question a bit "twisted"...
I've implemented in the past a lot of data structure (tree, list, hash table, graph as well), using the macro i can implement some kind o generic. However i was wandering if it is possible to implement generic data structure using void pointer but somehow i would like to be able to use a typecheking...
I don't know if it is clear what i'm trying to say... but basically i don't think it is always safe to put "void*" as generic, at the same time i don't think is always a good idea to use the macro as way to make a generic data structure (since basically what a preprocessor does with the macro does is code substitution), because if you look around the web you can find such examples.
A good idea might be, in my opinion but probably i'm not right, is to use the macro for make a standard interface for the data stored in a data structure, among the interface functions i would put code for correct type checking, given a void*. Inspired by the software engineer techinique this could be a good way to proceed.
It is surely true that probably for too sofisticated stuff would be better to switch language (C++/Java) but it is even true that this is not always possible.
So in summary... how the problem of "generic" in C is usually handled? i rely on your experience for an answer!
// void pointer void *ptr; double d = 9.0; // valid code ptr = &d; The void pointer is a generic pointer that is used when we don't know the data type of the variable that the pointer points to.
void (C++) If a pointer's type is void* , the pointer can point to any variable that's not declared with the const or volatile keyword. A void* pointer can't be dereferenced unless it's cast to another type. A void* pointer can be converted into any other type of data pointer.
No difference. void pointer is itself called generic pointer.
There is only one drawback, which is that polymorphism based on void * is unsafe: once you cast a pointer to void *, there is nothing that prevents you from casting that void * to the wrong pointer type by mistake.
Briefly, there’s no convenient way to get type-safe generic data structures and functions in C.
Non-generic:
struct node {
int value;
struct node *next;
};
Generic, but unsafe—a void*
has no type information:
struct node {
void *value;
struct node *next;
};
Safe, but ugly:
#define DECLARE_NODE_TYPE(type) \
struct node_##type { \
type value; \
struct node_##type *next; \
};
DECLARE_NODE_TYPE(int)
node_int *x = ...
Same idea, but slightly less ugly:
// declare_node_type.h
struct node_##NODE_TYPE {
NODE_TYPE value;
struct node_##NODE_TYPE *next;
};
#undef NODE_TYPE
// elsewhere
#define NODE_TYPE int
#include "declare_node_type.h"
node_int *x = ...
Generic and safe, but C++, not C:
template<typename T>
struct node {
T value;
node<T> *next;
};
node<int> *x = ...
You can do safer stuff with void*
; getting back to the linked example of Jon Purdy:
typedef struct {
union {
void* data; // generic data
int idata; // int is not stored dynamically
};
int type; // additional type information
Node* next; // link
} Node;
#define NODE_TYPE_INT 0
Node* createNodeInt(Node* self, Node* next, int value) {
self->idata = value;
self->type = NODE_TYPE_INT;
self->next = next;
return self;
}
// in this case relying on user defined types...
Node* createNodeGeneric(Node* self, Node* next, void* data, int type) {
assert(type != NODE_TYPE_INT && ..);
self->data = data;
self->type = type;
self->next = next;
return self;
}
Another approach is to use the common first member as the base type:
typedef struct {
int type;
} Node;
#define TYPE_BINARY 0
typedef struct {
Node base;
Node* left;
Node* right;
int op;
} BinaryOp;
#define TYPE_LEAF_INT 1
typedef struct {
Node base;
int a;
} LeafInt;
#define TYPE_LEAF_FLOAT 2
typedef struct {
Node base;
float b;
} LeafFloat;
void op(BinaryOp* node) {
switch(node->left.type) {
case TYPE_BINARY:
op((BinaryOp*)node->left);
break;
case TYPE_LEAF_INT:
evalInt((LeafInt*)node->left);
break;
...
}
}
Node* foo() {
LeafInt* left;
LeafFloat* right;
BinaryOp* op;
// allocate
...
// init
createLeafInt(left, 42);
createLeafFloat(right, 13.37);
createBinaryOp(op, &left->base, &right->base);
// and return
return &op->base;
}
}
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