2015-09-13

The Paper Crane and the Endless Struggles of a Man in a System of Endless Struggle.

"So what you are saying is that the sky will not fall?"

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.

2 kommentarer:

  1. I have some similar pattern here [0]/[1].
    [0] https://github.com/bulk77i/bulk.c
    [1] https://github.com/bulk77i/bulk.h

    SvaraRadera
  2. just keep it simple dude

    #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);
    }

    SvaraRadera