5
\$\begingroup\$

I made a very simple generic function to foreach over an array in C using a callback function:

#include <stddef.h>
void foreach_array(void* array, size_t size, size_t bytes_per_element, void (*callback)(void*)) {
    char* iter;
    for(iter = array; iter < (char*)array + (int)size; iter += (int)bytes_per_element) {
        callback((void*)iter);
    }
}

It can be used like:

#include <stdio.h>
void print_double(void* doubleToPrint) {
    printf("%f\n", *((double*)doubleToPrint));
}

int main() {
    double myArray[] = {3.0, 6.0, 9.0};
    foreach_array(myArray, sizeof(myArray), sizeof(*myArray), print_double);
}

Comments:

  • I had to use char* instead of void* for the iteration variable, Because the standard doesn't define pointer arithmetic on void*.
  • I typecasted the size and bytes_per_element arguments to an int because I wasn't sure if it was defined behavior to use pointer arithmetic using a size_t value.

I thought this foreach function was interesting, So I wanted to put it here for a code review.

\$\endgroup\$
1
\$\begingroup\$

Well, it's unavoidably quite complex to use, you need to provide 3 arguments to describe the array (start, size, element_size) and a function accepting a single element as a void*-argument:
Writing the loop manually is far simpler.

To top it of, you cannot pass any context to your callback, meaning it can only work on global state. That's all kinds of unfortunate.

Now, let's look at your implementation:
You are far too cast-happy.

  • The casts to int are wrong, SIZE_MAX can be bigger than INT_MAX. Anyway they are superfluous because yes you can use a size_t for pointer-arithmetic.
  • The cast to void* is superfluous, it would be implicitly converted.

Incorporating that all gives you:

#include <stddef.h>
void foreach_array(
    void* array,
    size_t size,
    size_t bytes_per_element,
    void (*callback)(void* context, void* element),
    void* context
) {
    for(char* iter = array; iter < (char*)array + size; iter += bytes_per_element)
        callback(context, iter);
}
\$\endgroup\$
  • \$\begingroup\$ How would I use the context parameter? \$\endgroup\$ – PointerToConstantChar May 4 '17 at 23:14
  • \$\begingroup\$ It's a pointer for passing any additional info to the callback. Just point it at whatever is needed. \$\endgroup\$ – Deduplicator May 5 '17 at 0:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.