I'm currently working on a project that require me to use some linked lists.
I'm used to them but I really don't like the fact that I need to duplicate my code.
Here is what i mean :
struct A {
struct A *prev;
struct A *next;
int i;
}
struct B {
struct B *prev;
struct B *next;
char *str;
}
If I want to create a function that add an element to any of my liked list i would need to do something like :
void add_A_element(struct A *list, struct A *new_element) { [...] }
void add_B_element(struct B *list, struct B *new_element) { [...] }
Here is my question, is there a way for me to have only one function for all my structures ?
I'm wondering if i could do so with an other structure like :
struct template {
struct template *prev;
struct template *next;
}
Then the function to add my element would look like :
void add_element(void *list, void *new_element)
{
struct template tmp_list = (struct template *)list;
struct template tmp_new_element = (struct template *)new_element;
for (struct template tmp = tmp_list; tmp != NULL; tmp = tmp->next) {
if (tmp->next == NULL) {
tmp->next = tmp_new_element;
tmp_new_element->prev = tmp;
break;
}
}
return;
}
Because we modify the same memory space, i guess this could work but i think some unexpected issue could happen.
I'm wondering if something that looks like a bit more like C++ template could exist in C.
Any help would be appreciate. Thanks
Edit : I managed to do it. I'm still planning on adding few things such as fifo/lifo queue but you can already get it on my Github
I've got full solution with templates (macro) in C if you'd like to have a look, links in the end. Meanwhile, let me explain how to approach that problem:
You should use container_of strategy to abstractly traverse a linked list.
struct linked_list_head
{
struct linked_list_head * next;
struct linked_list_head * prev;
};
struct my_type {
...
struct linked_list_head head;
}
Then the code to get the next would be
struct linked_list_head next(struct linked_list_head * current) {
return current->next;
}
struct my_type next = container_of(next(¤t.head), struct my_type, head));
What is the container_of macro?
Understanding container_of macro in the Linux kernel
If you want to see a full solution using templates implemented, I got a free to use (at your own risk) solution:
The library: https://github.com/flplv/fl-lib/blob/master/headers/linked_list.h
The Usage: https://github.com/flplv/fl-lib/blob/master/tests/test_linked_list.cpp
Related
Let's assume there is an employee ADT, such as
//employee.h
typedef struct employee_t employee_t;
employee_t* employee_create(char* company, char* department, char* position);
void employee_free(employee_t* me);
, and client code would be
#include "employee.h"
employee_t* Kevin = employee_create("Facebook", "Marketing", "Sales");
employee_t* John = employee_create("Microsoft", "R&D", "Engineer");
Now client wanted to use list ADT to insert Kevin and John to list for some task.
//list.h
typedef struct list_t list_t;
list_t* list_create(/*might have some arguments*/);
So client code would then be
#include "employee.h"
#include "list.h"
employee_t* Kevin = employee_create("Facebook", "Marketing", "Sales");
employee_t* John = employee_create("Microsoft", "R&D", "Engineer");
list_t* employee = list_create(/*might have some arguments*/);
list_insert(employee, Kevin);
list_insert(employee, John);
employee_free(Kevin);
employee_free(John);
list_print(employee); //Oops! How to print structure that you can't see?
Because employee is encapsulated by opaque pointer, there is no way for list to copy it.
How to write ADT and implementation for list?
The usual way to do this is to have your list structure store the data as a void*. For example, assmuming your list is a singly linked list:
struct list_t
{
void *data;
struct list_t *next;
};
Now list_insert whould be something like this:
list_t *list_insert(list_t *head, void *data)
{
list_t *newHead = (list_t*)malloc(sizeof(list_t));
newHead->data;
newHead->next = head;
return newHead;
}
If you want to hide away the implementation of the struct then you can add methods to extract the data. For example:
void *list_get_data(list_t *head)
{
return head->data;
}
How do you write generic list without knowing the implementation of structure?
Create functions that handle the structure abstractly.
How to write ADT and implementation for list?
list_create(); needs to pass in helper function pointers for the particular object type to perform various tasks abstractly.
A copy function like void *employee_copy(const void *emp) so list_insert(employee, Kevin); knows how to copy Kevin.
A free function like void employee_free(void *emp) so list_uninsert(employee_t) can free the list when destroyed or members removed one-by-one.
A print function int employee_print(void *emp) so list_print(employee_t) knows how to print each member of its list.
Possibly others.
Rather than pass in 3+ function pointers, consider passing in a struct that contains these pointers, then the list only needs the overhead of 1 pointer: list_create(employee_t_function_list)
You are taking your first steps toward re-writing C++
You can use something called intrusive list. This concept is heavily used in Linux kernel.
All you need is to embed the node into the struct and let the generic code operate only on this struct member.
#include <stddef.h>
struct list_node {
struct list_node *next;
};
struct list_head {
struct list_node *first;
};
/* translates pointer to a node to pointer to containing structure
* for each pointer `ptr` to a `struct S` that contain `struct list_node node` member:
* list_entry(&ptr->node, S, node) == ptr
*/
#define list_entry(ptr, type, member) \
(type*)((char*)ptr - offsetof(type, member))
void list_insert(struct list_head *head, struct list_node *node) {
node->next = head->first;
head->first = node;
}
#define LIST_FOREACH(it, head) \
for (struct list_node *it = (head)->first; it; it = it->next)
The interface can be easily extended by other helpers like list_is_empty, list_first, list_remove_first, embed size to struct list_head.
Exemplary usage:
typedef struct {
char *name;
struct list_node node;
} employee_t;
typedef struct {
char *name;
struct list_head employees;
} employer_t;
employer_t company = { .name = "The Company" };
employee_t bob = { .name = "Bob" };
employee_t mark = { .name = "Mark" };
list_insert(&company.employees, &bob.node);
list_insert(&company.employees, &mark.node);
printf("Employees of %s:\n", company.name);
LIST_FOREACH(n, &company.employees) {
employee_t *e = list_entry(n, employee_t, node);
printf("%s\n", e->name);
}
Prints:
Employees of The Company:
Mark
Bob
Note that the list_* interface can easily used for other types as well.
See article for more information about using this concept for double-linked list.
Edit
Note that list_entry invokes a subtle Undefined Behavior.
It is related to performing pointer arithmetics outside of the struct member object but still within a parent object.
Note that any objects can be treated as an array of chars.
This code will work on all major compilers and it very unlikely to ever fail because it would break a lot of existing and heavily used code (like Linux kernel or Git).
This program is strictly conforming if struct node is a first member of the embedding struct because C standard allows safe conversion between any structure and its first member.
To be strictly conforming if node is not a first member,
The issue could be circumvented by forming a pointer to struct list_node not as &bob.node but rather using a pointer arithmetics on a pointer to bob. The result would be:
(struct list_node*)((char*)&bob + offsetof(employee_t, node))
However, this syntax is really nasty, so personally I would go for &bob.node.
Thanks to this awesome answer I have mostly got how the Linux kernel linked lists work. However, the code examples in the answer are not 100% clear, and based on those alone I have written some code that gives me some issues when iterating the list.
In my module, I've initialized the list as a
struct list_head list_of_somethings;
in my __init function, I initialize it like this (I don't really get how the LIST_HEAD macro has to be used):
list_of_somethings.next = &list_of_somethings;
list_of_somethings.prev = &list_of_somethings;
This is how I add an element to the list:
struct something *new_entry = kzalloc(sizeof(struct something), GFP_KERNEL);
new_entry->some_datum=1;
// other new_entry params are set
// [...]
list_add(&(new_entry->list), &list_of_somethings);
Where struct something is defined as:
struct something {
struct list_head list;
uint8_t some_datum;
// other data
// ...
};
This is how I iterate all elements in the linked list to find the one I need:
struct list_head *pos;
struct list_head *head = &list_of_somethings;
struct something *seeked = NULL;
list_for_each(pos, head) {
struct something *pos_entry = container_of(pos, struct something, list);
if (pos_entry->some_datum == 1) {
seeked = pos_entry;
break;
}
}
And this is how I free the whole list on __exit:
struct list_head *pos;
struct list_head *head = &list_of_somethings;
list_for_each(pos, head) {
struct something *pos_entry = container_of(pos, struct something, list);
// free eventual dynamically allocated structures which are fields of pos_entry
kfree(pos_entry);
}
What am I doing wrong? I get general protection faults when I try to delete the whole list, and I don't get what I expect when retrieving an element from the list. Could you please help me understand how these should be done?
For a project i am expected to create an empty linked list in one function, and then use that linked list in another function to add items into it. At the moment this is my code for the empty linked list:
typedef struct node_t {
int value;
int priority;
struct node_t *next;
}node;
typedef struct priorty_linked_list {
struct name *head;
int current_size;
int max_size;
}priority_list;
typedef node *Node;
typedef priority_list *List;
Our instructor gave us the above code, so there shouldnt be anything wrong with it. Next i started with the function create:
void create(int max_terms) {
node *head = NULL;
node *next = NULL;
List *current_size = 0;
List *max_size = max_terms;
max_size = (List*)malloc(sizeof(List));
printf("The maximum size for the list is %d",max_terms);
}
I'm assuming that the next function requires me to use a return function from the create function, but im not sure how. The add function should take the queue created above as a parameter, and not work if i havent created a queue before due to memory allocation.
Any tips or advice on my above code would be greatly appreciated!
Thanks :)
I'm going to guess that in the definition of priorty_linked_list, you wanted to write:
struct node *head;
instead of:
struct name *head;
This site has a great explanation and decent implementation that can be used for reference. There are thousands of examples in the Web very similar to what you have to implement. Don't be afraid to Google it.
I have in the existing source base, linked list implementation(adding node, insertion, deletion , traversal) for the following structure:
typedef struct tagDirInfo
{
char *pdirName;
struct tagDirInfo *__next;
struct tagDirInfo *__prev;
}DIR_HEADER;
Lets assume that char* pdirName points to the data part
I want to form a wrap up for the data part and reuse the existing APIs and so that, the new linked list structure has the data part as:
typedef struct printJob
{
char labelName[BUF_LEN];
int priStatus;
time_t time_stamp;
}PRINTJOB;
I think if I do something like:
PRINTJOB newJob;
/* Fill in newJob structure */
DIR_HEADER *newNode;
newNode->pdirName = (char*)newJob;
newNode->__next = NULL;
newNode->__prev = NULL;
Doing so, will fill in the linked list structure.
But how can I access labelName data field through pdirName field of the linked list structure?
Do you mean you want do something like :
printf("labelName : %s\n", ((PRINTJOB *)(newNode->pdirName))->labelName);
However, your code have one mistake! To correct it:
Change
newNode->pdirName = (char*)newJob;
to
newNode->pdirName = (char*)&newJob;
You should use templates (if you could use c++).
char* Labelname = ((PRINTJOB*) newNode->pdirName)->labelName;
By the way, "newJob" should be of type PRINTJOB* not PRINTJOB.
A better solution would be following:
typedef struct _LINKED_LIST {
struct _LINKED_LIST *_Next;
struct _LINKED_LIST *_Prev;
} LINKED_LIST;
typedef struct {
LINKED_LIST List;
char labelName[BUF_LEN];
int priStatus;
time_t time_stamp;
} MY_LINKED_LIST_DATA;
MY_LINKED_LIST_DATA* MyData = (MY_LINKED_LIST_DATA*)
malloc(sizeof(MY_LINKED_LIST_DATA));
MyData->List->_Next = NULL;
MyData->List->_Prev = NULL;
Your data always contains linked list specific fields _Next and _Prev.
I am a beginner in programming, please go easy on me and I am finding difficult to get the answer for my question. I can't get my head around the complex codes. Can some one please explain me with simple coding of how is generic list manipulation function written which accepts elements of any kind? Thanks in advance.
This is normally done using void pointers:
typedef struct node {
struct node *next;
void *data;
} node;
node *insert(node *list, void *data) {
}
node *delete(node *list, node *to_delete) {
}
such manipulation functions do not depend on the actual type of data so they can be implemented generically. For example you can have a data type struct for the data field above:
typedef struct data {
int type;
void *data;
} data;
/* .... */
data d;
d.type = INT;
d.data = malloc(sizeof(int));
node n = {NULL, (void*)&data);
It looks like you need a heterogenous list. Some pointers below:
Make the data element of the list node as a generic structure, which contains an indicator for data type and data.
/** This should be your data node **/
struct nodedata
{
int datatype;
void *data;
};
/** This should be your list node **/
struct listnode
{
struct nodedata *data;
struct listnode *next;
};
Using the above structure, you can store different types of data.
Use function pointers for comparison functions or invoke different functions depending upon the data type.