Generic data structure search in c - c

I'm trying to code a fully generic data structure library in c.
Is there any way or technique in c programming that allows searching data without knowing its type?
Here I have to define my compare function again upon my data type.
list.h
typedef struct _node
{
void *data;
struct _node *next;
}NODE;
typedef struct _list
{
NODE *head;
NODE *tail;
NODE *current;
}GLIST;
int search(GLIST *list,void *data,int (*COMPARE)(void*,void*));
and
list.c
int search(GLIST *list,void *data,int(*COMPARE)(void*,void*))
{
list->current=list->head;
int cIndex=1;
while(list->current)
{
if(COMPARE(list->current->data,data))
{
printf("data found at position %i.\n",cIndex);
if(list->current->next==NULL)
{
return 1;
}
}
list->current=list->current->next;
cIndex++;
}
printf("NO DATA FOUND.\n");
return 0;
}
and
mycode.c
int compare(void *list,void *data);
typedef struct _student
{
int studentNumber;
char name[64];
}STUDENT;
int main()
{
GLIST list;
//initializing list......
STUDENT stud;
//code .....
search(&list,&stud,compare) // I want an alternative of using compare here
search(&list,&stud); // want the function be like this and also be generic !
return 0;
}
int compare(void *list,void *data)
{
// I do not wanna have to declare this function even
return !strcmp(((STUDENT*)list)->name,((STUDENT*)data)->name);
}
I'm wondering if there is A COMMON thing to compare elements "structures,unions,arrays" upon it in c or any technique else.

There is no way of comparing two objects without knowing their data type.
A first attempt would probably be to use something like memcmp, but this fails for at least three reasons:
Without knowing the type, you do not know the size of the object.
Even if you somehow could derive some size, comparing objects of type struct or union could lead to wrong result due to padding.
A comparison based on the memory layout could at most achieve a "shallow" comparison, which may not represent "equality" in terms of the respective data type.
So the only way (and this is used by generic libraries) is to define functions that accept user-defined comparison functions as parameters.

Related

Storing and using type information in C

I'm coming from Java and I'm trying to implement a doubly linked list in C as an exercise. I wanted to do something like the Java generics where I would pass a pointer type to the list initialization and this pointer type would be use to cast the list void pointer but I'm not sure if this is possible?
What I'm looking for is something that can be stored in a list struct and used to cast *data to the correct type from a node. I was thinking of using a double pointer but then I'd need to declare that as a void pointer and I'd have the same problem.
typedef struct node {
void *data;
struct node *next;
struct node *previous;
} node;
typedef struct list {
node *head;
node *tail;
//??? is there any way to store the data type of *data?
} list;
Typically, the use of specific functions like the following are used.
void List_Put_int(list *L, int *i);
void List_Put_double(list *L, double *d);
int * List_Get_int(list *L);
double *List_Get_double(list *L);
A not so easy for learner approach uses _Generic. C11 offers _Generic which allows for code, at compile time, to be steered as desired based on type.
The below offers basic code to save/fetch to 3 types of pointers. The macros would need expansion for each new types. _Generic does not allow 2 types listed that may be the same like unsigned * and size_t *. So there are are limitations.
The type_id(X) macros creates an enumeration for the 3 types which may be use to check for run-time problems as with LIST_POP(L, &d); below.
typedef struct node {
void *data;
int type;
} node;
typedef struct list {
node *head;
node *tail;
} list;
node node_var;
void List_Push(list *l, void *p, int type) {
// tbd code - simplistic use of global for illustration only
node_var.data = p;
node_var.type = type;
}
void *List_Pop(list *l, int type) {
// tbd code
assert(node_var.type == type);
return node_var.data;
}
#define cast(X,ptr) _Generic((X), \
double *: (double *) (ptr), \
unsigned *: (unsigned *) (ptr), \
int *: (int *) (ptr) \
)
#define type_id(X) _Generic((X), \
double *: 1, \
unsigned *: 2, \
int *: 3 \
)
#define LIST_PUSH(L, data) { List_Push((L),(data), type_id(data)); }
#define LIST_POP(L, dataptr) (*(dataptr)=cast(*dataptr, List_Pop((L), type_id(*dataptr))) )
Usage example and output
int main() {
list *L = 0; // tbd initialization
int i = 42;
printf("%p %d\n", (void*) &i, i);
LIST_PUSH(L, &i);
int *j;
LIST_POP(L, &j);
printf("%p %d\n", (void*) j, *j);
double *d;
LIST_POP(L, &d);
}
42
42
assertion error
There is no way to do what you want in C. There is no way to store a type in a variable and C doesn't have a template system like C++ that would allow you to fake it in the preprocessor.
You could define your own template-like macros that could quickly define your node and list structs for whatever type you need, but I think that sort of hackery is generally frowned upon unless you really need a whole bunch of linked lists that only differ in the type they store.
C doesn't have any runtime type information and doesn't have a type "Type". Types are meaningless once the code was compiled. So, there's no solution to what you ask provided by the language.
One common reason you would want to have a type available at runtime is that you have some code that might see different instances of your container and must do different things for different types stored in the container. You can easily solve such a situation using an enum, e.g.
enum ElementType
{
ET_INT; // int
ET_DOUBLE; // double
ET_CAR; // struct Car
// ...
};
and enumerate any type here that should ever go into your container. Another reason is if your container should take ownership of the objects stored in it and therefore must know how to destroy them (and sometimes how to clone them). For such cases, I recommend the use of function pointers:
typedef void (*ElementDeleter)(void *element);
typedef void *(*ElementCloner)(const void *element);
Then extend your struct to contain these:
typedef struct list {
node *head;
node *tail;
ElementDeleter deleter;
ElementCloner cloner;
} list;
Make sure they are set to a function that actually deletes resp. clones an element of the type to be stored in your container and then use them where needed, e.g. in a remove function, you could do something like
myList->deleter(myNode->data);
// delete the contained element without knowing its type
create enum type, that will store data type and alloc memory according to this enum. This could be done in switch/case construction.
Unlike Java or C++, C does not provide any type safety. To answer your question succinctly, by rearranging your node type this way:
struct node {
node* prev; /* put these at front */
node* next;
/* no data here */
};
You could then separately declare nodes carrying any data
struct data_node {.
data_node *prev; // keep these two data members at the front
data_node *next; // and in the same order as in struct list.
// you can add more data members here.
};
/* OR... */
enter code here
struct data_node2 {
node node_data; /* WANING: this may look a bit safer, but is _only_ if placed at the front.
/* more data ... */
};
You can then create a library that operates on data-less lists of nodes.
void list_add(list* l, node* n);
void list_remove(list* l, node* n);
/* etc... */
And by casting, use this 'generic lists' api to do operation on your list
You can have some sort of type information in your list declaration, for what it's worth, since C does not provide meaningful type protection.
struct data_list
{
data_node* head; /* this makes intent clear. */
data_node* tail;
};
struct data2_list
{
data_node2* head;
data_node2* tail;
};
/* ... */
data_node* my_data_node = malloc(sizeof(data_node));
data_node2* my_data_node2 = malloc(sizeof(data_node2));
/* ... */
list_add((list*)&my_list, (node*)my_data_node);
list_add((list*)&my_list2, &(my_data_node2->node_data));
/* warning above is because one could write this */
list_add((list*)&my_list2, (node*)my_data_node2);
/* etc... */
These two techniques generate the same object code, so which one you choose is up to you, really.
As an aside, avoid the typedef struct notation if your compiler allows, most compilers do, these days. It increases readability in the long run, IMHO. You can be certain some won't and some will agree with me on this subject though.

C language and Queues/linked lists

Can someone please explain the code below. I am new to C and trying to figure it out. why do we have queueNodeT at the end?
typedef char queueElementT;
typedef struct queueNodeTag {
queueElementT element;
struct queueNodeTag *next;
} queueNodeT;
queueNodeT is the name of the type that the typedef statement is trying to create.
An alternate way to specify this is:
struct queueNodeTag {
...
};
typedef struct queueNodeTag queueNodeT;
In C (vs. C++), "struct queueNodeTag" just defines a struct named "queueNodeTag". In C++ [should you get there], this would also define a type named "queueNodeTag"
When creating a pointer variable to the struct, it's slightly shorter to use:
queueNodeT *my_pointer;
than:
struct queueNodeTag *my_pointer;
The trailing "T" is just a coding convention to denote that it's a type name and not a variable. You can use others. Mine is:
struct mystructname {
...
};
typedef struct mystructname mystructname_t;
typedef mystructname_t *mystructname_p;
Using mystructname_p, you can change:
struct mystructname *my_pointer;
mystructname_t *my_pointer;
into:
mystructname_p my_pointer;
The "_t" is fairly common. The "_p" thing is my convention, but, I believe other conventions define pointers to types as "p<Mytype>", such as "pMystructName". I much prefer using a suffix for this [and "snake case" notation rather than the "camel hump" notation in your example].
Let's break it down part by part.
This line simply tells you that queueElementT is defined as char here. Meaning you can write either queueElementT or char, both will work.
typedef char queueElementT;
Now here is the actual struct. It contain two variables, the element it is holding, in this case a char. It then also tells which element is next in the queue.
typedef struct queueNodeTag {
queueElementT element;
struct queueNodeTag *next;
} queueNodeT;
More of that can be read in this answer.
A demonstration:
int count (queueNodeTag q) {
int i = 0;
if (q == null) {
return 0;
}
if (q.next == null) {
return 1;
}
while (q.next != null) {
q = q.next;
i++;
}
return i;
}
Three cases to handle.
q is null, the queue is empty. Return 0.
q.next is null, the queue only contains one element. Return 1.
Repeat until q.next is separated from null, increment i as we go. A better name for i might be elements or something similar.
This code is untested as I currently do not have a C compiler at hand. Someone who has one available can perhaps verify that no mistake has been done?

C - Functions Structs

So I am still pretty new to C programming. I have learned Python though, so I am familliar to some of the codes.
For instance when I create a function in python, I am able to make it general and usable for different classes.
I want to do something similar here. I have two structs which look practically the same. I want to use the same function for both structs, but ofcourse I cant send in the struct name as an argument into the function. What do I do instead?
For now dont worry about what the function does. Its the principle of being able to use two structs in the same function that counts for me. If this is a totally wrong perspective, then I am sorry but this was my first thought when coming upon this problem.
typedef struct{
int number;
struct node *next;
}struct_1;
struct node *head;
typedef struct{
int number;
struct node *next;
}struct_2;
void main()
{
int number1 = 10;
int number2 = 20;
function(number1);
function(number2);
}
void function(int x, struct) // Here is where I want to be able to use 2 different structs for the same function
{
struct *curr, *head;
curr=(node1*)malloc(sizeof(node1));
printf("%d", curr->number);
}
You could have two instances of one structure.
The function can accept either instance and process it as needed.
typedef struct{
int number;
struct node *next;
}mystruct;
void function(int x, mystruct *eachstruct);//prototype
int main()
{
int number1 = 10;
int number2 = 20;
//declare two instances of mystruct
mystruct list_1 = { 0, NULL};
mystruct list_2 = { 0, NULL};
// call the function with one or the other instance of mystruct
function(number1, &list_1);
function(number2, &list_2);
}
void function(int x, mystruct *eachstruct)
{
//do stuff in function
eachstruct->number = x;
if ( eachstruct->next == NULL)
{
//do more stuff
}
}
C does not use duck typing as Python does so you cannot pass one structure that looks like other, completely unrelated structure as if it was this other structure.
Unfortunately C cannot do what you want.
Your options are:
Refactor the code to use the same struct type for all items.
Pass the fields of interest in the structs directly to the functions
Write code to marshal the similar structs to a common struct.
Play fast and loose with the type system and arrange shared elements the same way in the two different structs and cast your pointers.
If you just want a linked list check out how code re-use is achieved in the linux kernel
Answer: No, you cannot do it directly. Welcome to static typing.
There is a way to achieve something similar by using our beloved void * and some castings but, believe me, it is not what you want to do. If you really want to do it ask directly for it. You have been warned.

generic data structure in C [duplicate]

This question already has answers here:
Simulation of templates in C (for a queue data type)
(10 answers)
Closed 6 years ago.
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.
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);
}
}
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.
uthash is a collection of header files that provide typed hash table, linked list, etc. implementations, all using C preprocessor macros.

list_entry() in list.h returning a warning

Consider this piece of code:
struct Trade
{
float Price;
char* time;
int shares;
struct list_head *tradeList;
};
typedef struct Trade Trade;
void DisplayTrades(Trade* TBook)
{
if(TBook==NULL)
{
printf("NO TRADES\n");
return;
}
struct list_head *pos;
Trade *tmp;
pos = TBook->tradeList;
__list_for_each(pos,TBook->tradeList)
{
tmp = list_entry((pos),Trade,tradeList);
printf("Price %f, Time %s, Shares %d\n",tmp->Price,tmp->time,tmp->shares);
}
}
In this code, when I compile, the compiler gcc returns a warning that initialization from incompatible pointer type in the line where list_entry is evoked.
I have used list_entry in other places of the same code, and its working without any glitch. So the only thing that struck me was perhaps I passed unintended variable types to the function, hence attached the definition of structure Trade.The problem persists even then.
Would appreciate,to know where things are going wrong.
EDIT : This is just a small snippet from an otherwise large code. I apologise for making it look like, that I am trying to use Trade* object when none exists. In the code, I have indeed used typedef to define struct Trade;
For this to work, pos must be an actual list head pointer, but the structure field shoud be a list_head and not a list_head pointer :
struct Trade
{
float Price;
char* time;
int shares;
struct list_head tradeList;
};
And then :
void DisplayTrades(Trade* TBook)
{
if(TBook==NULL)
{
printf("NO TRADES\n");
return;
}
struct list_head *pos;
Trade *tmp;
__list_for_each(pos,&TBook->tradeList)
{
tmp = list_entry((pos),Trade,tradeList);
printf("Price %f, Time %s, Shares %d\n",tmp->Price,tmp->time,tmp->shares);
}
}
In DisplayTrades function while declaring the pointer of Trade structure as its argument you must use
struct Trade * Tbook.

Resources