I am a C beginner with quite a lot of OOP experience (C#) and I am having trouble understanding how some notion of "polymorphism" can be achieved in C.
Right now, I am thinking how to capture the logical structure of a file system using structs. I have a folder that contains both folders and files. Folders in this folder can contain another files and folders, etc.
My approach:
typedef enum { file, folder } node_type;
struct node;
typedef struct {
node_type type;
char *name;
struct node *next;
struct node *children;
} node;
Is this the best I can do? I have found a lot of posts on "polymorphism in C", but I would like to see how a polymorphic data structure like this can be built cleanly and efficiently (in terms of memory wasted on unused members of those structures).
Thanks.
I hope I understand what you want - I'm unsure but I guess you want to do something like that:
typedef struct
{
int type; // file or folder?
} Item;
typedef struct
{
struct A;
// data related to a file
} File;
typedef struct
{
struct A;
// data related to a folder - like pointer to list of Item
} Folder;
As long as both structure follow the same memory mapping (same variables) and adds to it as a child, you'll be able to use the pointer properly in both structs.
Check this one out as well: How can I simulate OO-style polymorphism in C?
Edit: I'm not sure about the syntax above (took it from the link above). I'm used to writing it this way instead:
typedef struct
{
int type;
// data for file
} File;
typedef struct
{
int type;
// data for folder - list, etc
} Folder;
C has no intrinsic notion of polymorphism.
You will end up implementing the mechanisms that you want from scratch. That's not a bad thing. It gives you a lot more flexibility. For example, C++ virtual methods are hard-wired per class, you can't change method pointers per-instance.
Here are a few ideas:
Your node_type field provides a way to do a runtime type query. Going further, you can pack multiple types into one struct using a discriminated (or tagged) union: http://en.wikipedia.org/wiki/Tagged_union. I'm not sure whether a variant type qualifies as OO though.
Polymorphism is usually about behavior. You could store function pointers ("methods") in the struct, with pointers to different functions providing different behavior for different object instances. The C++ way of doing things is for each class to have a table of function pointers, then each object instance references the table for its class (incidentally the table pointers can also play the role of your node_type for RTTI). This is called a virtual method table.
Data inheritance means that subclasses contain all of the base class' data members plus some extra stuff. In C the easiest way to do this is by embedding the base class struct at the head of the derived class struct. That way a pointer to derived is a pointer to base.
typedef struct BaseClass {
int baseMember;
} BaseClass;
typedef struct DerivedClass {
BaseClass base;
int derivedMember;
} DerivedClass;
You could do worse than read "Inside the C++ Object Model" by Stanley B. Lippman. For example, this will help if you want to get an idea of how to implement multiple inheritance.
Here's an illustration of old-school C polymorphism, based on ancient memories of X/Motif.
If you just want a discriminated union (or even just a typed structure with a child pointer that may be null), it's probably simpler in your case.
enum NodeType { TFile, TFolder };
struct Node {
enum NodeType type;
const char *name;
struct Node *next;
};
struct FileNode {
struct Node base_;
};
struct FolderNode {
struct Node base_;
struct Node *children;
/* assuming children are linked with their next pointers ... */
};
Here are the constructors - I'll leave populating the linked lists as an exercise for the reader ...
struct Node* create_file(const char *name) {
struct FileNode *file = malloc(sizeof(*file));
file->base_.type = TFile;
file->base_.name = name; /* strdup? */
file->base_.next = NULL;
return &file->base_;
}
struct Node* create_folder(const char *name) {
struct FolderNode *folder = malloc(sizeof(*folder));
folder->base_.type = TFolder;
folder->base_.name = name;
folder->base_.next = NULL;
folder->children = NULL;
return &folder->base_;
}
Now we can walk a hierarchy, checking the type of each node and responding appropriately. This relies on the first member subobject having zero offset to the parent - if that doesn't hold (or you need multiple inheritance), you have to use offsetof to convert between base and "derived" types.
void walk(struct Node *root,
void (*on_file)(struct FileNode *),
void (*on_folder)(struct FolderNode *))
{
struct Node *cur = root;
struct FileNode *file;
struct FolderNode *folder;
for (; cur != NULL; cur = cur->next) {
switch (cur->type) {
case TFile:
file = (struct FileNode *)cur;
on_file(file);
break;
case TFolder:
folder = (struct FolderNode *)cur;
on_folder(folder);
walk(folder->children, on_file, on_folder);
break;
}
}
}
Note that we have a sort-of-polymorphic base type, but instead of switching on the type enumeration we could have a more completely polymorphic setup with virtual functions. Just add a function pointer to Node, something like:
void (*visit)(struct Node *self,
void (*on_file)(struct FileNode *),
void (*on_folder)(struct FolderNode *));
and have create_file and create_folder set it to an appropriate function (say, visit_file or visit_folder). Then, instead of switching on the enumerated type, walk would just call
cur->visit(cur, on_file, on_folder);
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.
In my adventures implementing generic data structures in C, I've come across a dilemma. For example, in the following code:
void add_something(avl_tree_t * my_tree) {
int new_element = 123;
avl_insert(my_tree, (void*)&new_element);
}
int main() {
avl_tree_t * my_tree = avl_create();
add_something(my_tree);
// do stuff
avl_print(my_tree, function_that_prints_ints);
exit(0);
}
In which avl_insert is defined as
void avl_insert(avl_tree_t * tree, void * data) {
avl_node_t * new_node = malloc(sizeof(struct avl_node));
new_node->data = data;
// do tree balancing stuff
}
In order for my generic insertion function to work, I have to pass it a void * item to store. However, in order for that to work, in this case I need to pass in the address of the new int item I'm adding so that I can then dereference it to a void *. If I am not mistaken, when we're back in the main function, the memory address in which I stored my new element will be compromised.
One way I looked into to solve this issue is to pass in the size of the things I am storing in the tree as a parameter for avl_create, and then allocating memory for a copy of each element I insert. This works because you don't need the original address or value for whatever you added.
Another thing that works is only using the data structure in the span of a single function, which is obviously not viable.
My question is this: what is the best way to go about storing statically allocated data in a generic data structure, be it basic C types or user made structures?
Thank you in advance.
To store pointers to data with automatic storage duration, yes, you would have to know the size of the elements in the container and allocate and copy the pointed-to data.
The simplest way is to just allocate and copy in all cases, optionally using a user-specified clone() or create() function to make deep copies, if necessary. This also entails the use of a user-specified destroy() function to dispose of the copies properly (again, if necessary).
To be able to avoid the allocation, then you have to have some kind of state variable that lets you know if the container should allocate, or just copy the pointer value itself.
Note that this should apply to the container object, not to the individual nodes or elements. If a container stores data in one way or the other, it should store all data that way. See Principle of Least Astonishment.
This is the more complex approach, since you have to be sure to use the correct process for adding and deleting elements based on the state variable. It's ususally much simpler to just make sure you never pass in a pointer to a value with automatic storage duration.
Use a mix-in style; e.g. do not make data part of the node but the node part of the data:
struct avl_node {
struct avl_node *parent;
struct avl_node *left;
struct avl_node *right;
};
struct person {
char const *name;
struct avl_node node;
};
struct animal {
struct avl_node node;
int dangerousness;
};
Constructors for animal are like
struct animal *animal_create(double d)
{
struct animal *animal = malloc(sizeof *animal);
*animal = (struct animal) {
.node = AVL_NODE_INIT(),
.dangerousness = d,
};
return animal;
}
The generic AVL tree operations could look like
void avl_tree_insert(struct avl_node **root, struct avl_node *node,
int (*cmp)(struct avl_node const *a, struct avl_node const *b))
{
/* .... */
}
and a cmp function for animal like
int animal_cmp(struct avl_node const *a_, struct avl_node const *b_)
{
struct animal const *a = container_of(a_, struct animal, node);
struct animal const *b = container_of(b_, struct animal, node);
return a->dangerousness - b->dangerousness;
}
I'm attempting to refactor some code, to reduce duplication.
This is a curated example, currently defined in each .c file.
struct hrentry_t
{
int custom1;
int custom2;
int custom3;
struct hrentry_t *prev, *next;
};
struct hrentry_t*
attachentry(struct hrentry_t* hentry)
{
struct hrentry_t* hnew = calloc(sizeof(struct hrentry_t), 1);
if (hnew == NULL)
return NULL;
if (hentry != NULL) {
while (hentry->next != NULL) {
hentry = hentry->next;
};
hentry->next = hnew;
hnew->prev = hentry;
}
return hnew;
}
https://github.com/techzilla/check_snmp_extras, is the entire codebase.
I'm declaring and initializing a custom double linked list, and corresponding allocate function. If I moved the linked list functions and code to the common lib .c and .h, how can I get the file specific data inside of each list entry? Each file requires different types and number of variables.
Maybe I make a the double linked list contain only prev next and data? Then somehow make data a handle to an incomplete struct? How would then that need to be allocated though? I'm open to fully reconsidering my approach, so solid advice from experienced coders is always appreciated.
One approach is to make your specialized list data types castable to a generic double-linked list structure. This can be accomplished by putting the non-specialized data members at the beginning of the structure:
struct node_t {
struct node_t * prev, * next;
};
struct hrentry_t
{
struct node_t node;
int custom1;
int custom2;
int custom3;
};
It then makes sense to cast an hentry_t* to a node_t*. The signature of your attachment function becomes:
struct node_t* attachentry(struct node_t* node);
And to use it, you cast instances of your specialized type to the generic type:
struct hentry_t * my_hentry_ptr; /* initialized somehow... */
my_list = attachentry((struct node_t*)my_hentry_ptr);
you are returning hnew if there is already a linked list present rather than the previous list with hnew added onto the tail of it.
return hrentry;
try this
I'm trying to program a network in C. I have nodes which are linked to each other and I 'd like to do that by making the struct member point to another member (not to another node, because I want to preserve the identity of the links).
The code I made to do that is something like:
struct node{
int k; //number of links
struct node.link **link; //<- wrong
};
but this is not right as node is not a variable but a type of variable (this is already discussed as an error in another QA: first you have to define a variable of node type and then apply the .link, but this doesn't help here). There's also a QA called "Struct member point at another struct member" but they don't do it from definition and it is not so clear how to generalize it (at least for me).
Is it a correct way to do this?
The problem is that the C language doesn't let you create the type you want. You need a type T with the property *T has the same type as T. You can't do that. (Well, function pointers have that property, but that's an irrelevant technicality.)
You have to introduce a new name. C only lets you do this with structs or similar constructions.
struct link {
struct link *ptr;
};
struct node {
int k;
struct link *link;
};
This will get you what you want. Now, in order to go from a struct link * to a struct node *, you'll have to do some pointer math:
struct node *node_from_link(struct link *link) {
return (struct node *) ((char *) link - offsetof(struct node, link));
}
This is also provided by the container_of macro, which is not part of the C standard, but you can find a definition for it online.
Or, you could just go the traditional route.
// Usually easier to do it this way...
struct node {
int k;
struct node *link;
};
Is this what you are after?
struct Node
{
int k; //number of links
void* link;
};
struct Node* create()
{
struct Node* node = malloc(sizeof(struct Node));
node->k = 0;
node->link = 0;
return node;
}
void link(struct Node* from, struct Node* to)
{
from->link = &(to->link);
}
int main()
{
struct Node* child = create();
struct Node* parent = create();
link(parent, child);
return 0;
}
I've used void* for the link for the reason expressed by Dietrich: you want a pointer to the link to be the same type as the link. This effectively means a cast, so why not just use a generic pointer?
Membership in a structure, generalized or specific, is not an attribute of C data types. There is therefore no way to declare a pointer that can only point to a structure member, and not to any other variable of compatible type.
On the other hand, you don't need to do anything special to declare a pointer that can point to a member of another structure. You just need a pointer to that member's data type, and structure membership is irrelevant to that data type.
For example, you can have
struct node {
int k; /* number of links */
struct node **links; /* points to a dynamic array of node pointers */
struct node **one_link; /* points to a node pointer from another node */
};
In that case, it might make sense to do something like this:
struct node *n1 = /* ... */;
struct node *n2 = /* ... */;
n2->one_link = &(n1->links[3]);
Overall, though, I think this is kind of convoluted. There is probably a better way to structure your data.
Update:
Based on your description of what you're after:
[...] links are bidirectional, if I destroy one link (say the one that links node 1 to node 3) I'll need to destroy the node 1 link AND the corresponding link from node 3. Then I need to know more than just who is link to who. I need to know which link they are using.
there are at least two possible solutions, depending on details of how your nodes are structured. If they are structured like I show above, with an array (dynamic or not) of pointers to other nodes, then your general idea simply won't work. That's because the position of each link within an array of links will change as you delete other links (supposing that you close the gaps). Instead, you can just scan:
struct node {
int k; /* number of links */
struct node **links; /* points to a dynamic array of node pointers */
struct node *parent; /* points to a node that links to this one */
};
void delete_node(struct node *n) {
if (n->parent) {
int i;
for (i = 0; i < n->parent->k; i += 1) {
if (n->parent->links[i] == n) {
/* ... delete the ith element of n->parent->links ... */
break;
}
}
}
/* ... clean up node n ... */
}
If one node's links to others are stored in separate members, on the other hand, then you could indeed provide a double-pointer by which to remove links from the parent, but the presence of member k in your original structure tells me that's not your situation.
Ok, this is how I finally solved it in my program:
typedef struct node{
int k; //connectivity
struct link **enlace; //vector of LINKs
}NODE;
typedef struct link{
NODE *node1;
NODE *node2;
}LINK;
Basicly, I defined two structures: one is the NODE type, which contains the information of how connected is the node and a vector of LINKs, and the other is the structure LINK which contains the information of the link itself, I mean which nodes the link connects.
With these two I'm able to create the network of nodes with a connectivity following a Poisson distribution, and then destroy each link one by one, choosing one link at random from a list and then redirecting the pointers of each node to NULL.
The idea: a list of lists (for a publisher-suscriber problem)
The problem: the lists use different types of structs as nodes. So my function does not work with the second type of struct (that's just fine, I could simply create another function and just change the parameters that it uses).
But I feel that's a rather simplistic approach and honestly the amount of code I'm beggining to handle is a bit too much.
Is there a more professional/experienced way to do this? (that I can handle. For example here they talk about it but I'm not quite sure I could implement it without messing up since I've never used unions:
How to Pop different types of Structs from a Stack
)
Is this a common/aceptable way of doing things (reusing functions for different structs/data types)?
Structs I use:
struct nodoTemas{
char * nombreTema;
struct nodoSuscriptor * nodoCerodeSuscriptores;
struct nodoTemas * next;
};
struct nodoSuscriptor{
char * nombreSuscriptor;
//int iP;
struct nodoTemas * next;
};
So I have a working code with a function that creates the list , and other similar methos to interact with it.
struct nodoTemas* create_list(char * val, struct nodoTemas * head, struct nodoTemas *curr)
{
printf("\n creating list with headnode as [%s]\n",val);
struct nodoTemas *ptr = (struct nodoTemas*)malloc(sizeof(struct nodoTemas));
if(NULL == ptr)
{
printf("\n Node creation failed \n");
return NULL;
}
ptr->nombreTema = val;
ptr->next = NULL;
head = curr = ptr;
printf("Ha llegado aqui3\n");
return ptr;
}
I provided this much code because I'm usually asked to do so. As always I'm sorry if this is not the right place/the question is not worded properly.
EDIT: I have now find out that with union and struct, it'd hold space for the biggest of the two or more types. I am not sure if this is just wasting too much space and thus making that a not so good option, so not quite sure how to do it (the idea being that if if it's a list of suscribers, it could be 2 or 2000, and with each node added there'd be wasted space).
The problem you are struggling with - how to create reusable generic functionality, such as containers - is one which is addressed easily with object oriented programming languages such as C++. However they can be implemented in C, even if not as easily and flexibly.
The key is to identify which functionality is common and generic, and which is specific to the types of nodes in the list.
Generic functionality would be the functions such as creating/initializing the list, adding nodes, deleting nodes, iterating through the list, etc.
Create stand alone functions for handling lists, together with a struct that models the generic nodes.
Then, create structures representing the different object types in the list. Embed one of the generic list node structs as the first element in each of these structs, and write functions as needed to provide the functionality for handling each of the different types of objects you need to deal with.
In that way you end up with generic, re-usable lists, and as an added bonus you keep your code for dealing with the specific object types clean and easily maintainable.
Here is a simple program I whipped up to demonstrate the principle. It allows creating a list of objects representing apples and oranges. The apples have functionality for tracking their weight, and the oranges have functionality for tracking their price.
#include <stdio.h>
#include <stdlib.h>
/* First we start with the definition of a generic list node */
struct list_node {
struct list_node *next;
struct list_node *prev;
/* The 'type' field is important - it allows us to have a list
* containing a number of different types of node, and to be able
* to find out what type each node is */
int type;
};
/* Some variables to keep track of the beginning and end of the list. */
struct list_node* head;
struct list_node* tail;
/* Now some generic functions for dealing with lists - initializing the list,
* adding nodes through it, iterating through the list */
void list_init() {
head=tail=NULL;
}
void list_add_node(struct list_node* node) {
if (NULL==tail) {
head=tail=node;
node->next=NULL;
node->prev=NULL;
} else {
tail->next=node;
node->next=NULL;
node->prev=tail;
tail=node;
}
}
struct list_node* list_get_next(struct list_node* node) {
if (NULL==node)
return head;
else
return node->next;
};
/* Great, now we have a generic set of functions for dealing with generic lists.
* But how do we use that to contain different kinds of objects? The answer
* is composition - we include the list_node as the first element of each of the
* structs that we want to add to the list
*
*
* Here we define a struct for recording the weight of apples, together with
* functions specific to dealing with apples
*/
struct apple {
struct list_node node;
int weight;
};
struct apple* new_apple(int weight) {
struct apple* a = (struct apple*)malloc(sizeof(struct apple));
/* Apples are considered type 1 */
a->node.type = 1;
a->weight = weight;
return a;
};
void apple_printweight(struct apple* a) {
printf("This is an apple and it weighs %d grams\n", a->weight);
}
/* And here we define a struct for recording the price of oranges, together with
* functions specific for dealing with oranges
*/
struct orange {
struct list_node node;
double price;
};
struct orange* new_orange(double price) {
struct orange* o = (struct orange*)malloc(sizeof(struct orange));
/* Oranges are type 2 */
o->node.type = 2;
o->price=price;
return o;
};
void orange_printprice(struct orange* o) {
printf("This is an orange and it costs $%6.2f dollars\n", o->price);
}
/* Now to use our oranges and apples */
int main() {
list_init();
/* Create an orange, add it to the list
*
* Note the need to cast the orange to a list_node
* so we can call the 'list_add_node' function.
* This makes use of a property of pointers to structs:
* you can always convert them to point to the first element of
* the struct.
*/
struct orange* myOrange = new_orange(12.50);
list_add_node((struct list_node*)myOrange);
/* Create an apple, add it to the list */
struct apple* myApple = new_apple(150);
list_add_node((struct list_node*)myApple);
/* Iterate through the list */
struct list_node* n = NULL;
while (n = list_get_next(n)) {
/* For each node we come to, it could be an apple or an orange.
* Inspect the type to find out what type it is, and use it
* accordingly */
if (n->type == 1) {
apple_printweight((struct apple*)n);
} else if (n->type == 2) {
orange_printprice((struct orange*)n);
}
}
/* In a real program you would want to free the list objects here
* to avoid a memory leak
*/
}
Here is the output:
This is an orange and it costs $ 12.50 dollars
This is an apple and it weighs 150 grams
Bear in mind that this is just a simple example, and by no means does it illustrate best practice in all aspects. In real life you would separate the declarations into header files, and you would probably do something more elegant than the if/then branching in the main loop to handle the different types.
I'm struggling to find a reason why you're casting mallocs return value. It seems, if you want C++ compatibility, just nuke C and adopt C++'s linked lists and your life gets so much easier. As you're about to see, generics in C can be a pain in the butt! If you want to write C code, nuke the casts. If you want to reuse the C code in a C++ project, compile it using a C compiler and link to it using the compatible C++ compiler.
Yes, you'll want to eliminate redundant code. The first step to making your life that much easier is to eliminate malloc from your linked list code. Pass all allocated data in via arguments, just like sprintf requires. There is a reason sprintf was designed like that. Liberation of data-specific context goes hand-in-hand with liberation of allocation. That probably means your linked list struct will contain only one member: A pointer to the next node, eg struct linked_list_node { struct linked_list_node *next; };... So be it! You can declare new list types like so, and use the functions you've created to handle the generic list type:
struct apple_linked_list {
struct linked_list_node linked_list;
char *colour;
};
struct apple_linked_list_linked_list {
struct linked_list_node super_list;
struct apple_linked_list_node *sub_list;
};
struct apple_linked_list red = { .colour = "red" },
blue = { .colour = "blue" },
green = { .colour = "green" },
*root = NULL;
root = linked_list_add(root, &red);
root = linked_list_add(root, &blue);
root = linked_list_add(root, &green);
struct apple_linked_list_linked_list list_list = { .sub_list = root };
struct apple_linked_list_linked_list *super_root = NULL;
super_root = linked_list_add(super_root, list_list);
So now you want malloc back. I can't say I blame you. It is a useful tool... but it's not to be inserted into your existing add function, because it's also useful to be able to create non-malloc'd lists. Kinda like how it's useful to be able to use sprintf without requiring malloc or free... Write a new struct to extend the old, and a new set of functions to operate on this struct. This gets a little more complex, but it's still possible. It might also be sensible to rename your previous types and functions to automatic_linked_list.
I've written all of this code and tested it, and I'm prepared to offer it to the public domain here.
Hope this helps :)