ForEach in C
In C we do not have iterators in any sane fashion (C++'s iterators are not sane either, btw 😜 ) nor do we have a standard array_for_each (how would that even work?). So when implementing a C data structure that may need for_each capabilities it is best to do it. Relying on for loops can be problematic if you are not careful.
In these example I will use a DynamicArray as the data structure. With functions to get items at an index and the length of the array. When it comes to LinkedLists you must use linked_list_next, linked_list_prev and linked_list_get_current, but that is outside this discussion.
First things firts: typedef:ing a standard ForEach function pointer
The signature for a good for each function in any implementation is as follows:
typedef void (ForEachFunc *)(void * self, void * item, void * user_data);
The self
here should be a pointer to the data structure you are doing the for
each on. No operations should de done on this structure.
The item
is the current item, the item that the for each function should operate.
The user_data
is any user data you can think of. It may be used as a out value
or as some other data that is used inside the for each function.
Implementing dynamic_array_for_each
In this example I will not rely on any internals of the data structure, instead I will use
dynamic_array_get()
and dynamic_array_len()
this means that you can implement this even if you do not have accesses to the underlying structure.
To begin with you must understand that this relies on a for loop, so it is not more efficient that just writing a for loop your self. It just provides a little bit safer way of coding, where you do not have to keep track of variables to much.
The signature of the function is as follows:
void dynamic_array_for_each (DynamicArray * self, ForEachFunc func, void * user_data) {}
The self
here is the dynamic array to perform the for-each function on.
The func
here is the function that we are to execute on each iteration.
The user_data
is the data to passed to the function. This may be used as an "output" of sorts.
void dynamic_array_for_each (DynamicArray * self, ForEachFunc func, void * user_data) { // First me get the length of the array, This should be the last item in the array. size_t len = dynamic_array_len (self); for (size_t i = 0; i <= len; i++) { // Check if the item is there. void * item = dynamic_array_get (self, i); if (item) { func (self, item, user_data); } } }
And that is it, folks!
Using it
So now it is now time to use the this for-each thingy. So lets define a function to use in the for-each.
void for_each_print (SDynamicArray * self, int item, int * itt) { fprintf (stdout, "The number is: %d on iteration: %d\n", item, (*itt)); (*itt)++; }
This function prints the number stored in the array and prints it out. as user data it takes a pointer to an int, this is that is incremented each time the function is called.
Lets now put this into a context:
int main (int argc, char ** argv, char ** argp) { /* create the array */ DynamicArray * arr = dynamic_array (); /* ... Code to fill the array with numbers. */ int itt = 0; dynamic_array_for_each (arr, (ForEachFunc)for_each_print, &itt); fprintf (stdout, "The last itt number is: %d", itt); /* ... Code to free the array and what not ... */ }
As you can see it is not too much work to implement or use.
Conclusion, or something.
So as we have seen it is not magic, it is actually easy to use, and it promotes code-re-use. One downside of this is that it does a lot of calls, and thus uses the call stack, this might be problematic if you you already are hitting the maximum call stack size.
But I firmly believe that this is the best way of doing things like this.
I have some similar pattern here [0]/[1].
SvaraRadera[0] https://github.com/bulk77i/bulk.c
[1] https://github.com/bulk77i/bulk.h
just keep it simple dude
SvaraRadera#define FOREACH(list) \
for(int i = 0; i < list->length; i++) \
for(void *elem = listGet(list, i); elem; elem = NULL)
next element of type void* will be in elem var,
index of elem you can get from i
using:
FOREACH(list) {
yourtype *var = elem;
somefunc(var, i);
otherfunc(var);
}