-1

I have the following code:

typedef struct ll_node linked_list_node;
struct ll_node {
  int value;
  linked_list_node* next;
};

typedef struct ll linked_list;
struct ll {
  size_t size;
  linked_list_node* head;
  linked_list_node* tail;
};

void for_each(linked_list* list, void (*function)(linked_list_node*, ...)) {
  if (list == NULL || list->head == NULL) {
    return;
  }

  for (linked_list_node* node = list->head; node != NULL; node = node->next) {
    function(node, function->__va_arg_pack());
  }
}

What I mainly want to achieve is for_each to receive a function that is executed to every single linked list node, but I'm not sure if that can be achieved with functions that receive multiple parameters... Please help!

  • 1
    What's that `function->__va_arg_pack()` stuff? It looks like it might be a part of C++; it isn't current standard C. – Jonathan Leffler Jul 03 '22 at 18:01
  • You have to pass all the arguments to the function being called by pointer. You can pass the node easily enough, but there are no other arguments available to pass. You can certainly pass multiple arguments to a function called via a function pointer. But it is not clear what you're up to. If you can invent some extra arguments, you can pass them. But it's an odd concept, rather like an [XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). What are you trying to do and why? – Jonathan Leffler Jul 03 '22 at 18:04
  • One standard technique is to modify your 'apply' function, perhaps like: `void apply(linked_list_node *list, void (*function)(linked_list *node, void *context), void *context) { if (list == NULL || list->head == NULL) { return; } for (linked_list_node* node = list->head; node != NULL; node = node->next) (*function)(node, context); } }` — I still prefer the explicit dereferencing of a function pointer (`(*function)(node, context)`) but you're allowed not to be so old-school. (The new school notation, `function(node, context)` was not an option when I learned C.) – Jonathan Leffler Jul 03 '22 at 18:10
  • The callback function is given a pointer to auxilliary data and can do what it likes with it. If you need a structure with multiple elements, so be it — can do! This avoids (helps avoid) the need for global variables. – Jonathan Leffler Jul 03 '22 at 18:11
  • After rereading my question I realized I didn't explain myself very well. I'm building a linked list library for C (just as a hobby), and I want that library to have a for_each function that mainly executes *something* (another function) to every single nodes in the linked list, so I thought implementing a variadic function might help for complex implementations. – Uriel Rivas Jul 03 '22 at 18:12
  • 1
    Use the version I showed with a 'context' parameter — that works for the scenario you want. It imposes some structure on the callback functions, but they can easily unpack the context to call other functions with multiple arguments directly. You might find some useful examples in the code available in my [SOQ](https://github.com/jleffler/soq) (Stack Overflow Questions) repository on GitHub as files `aomcopy.c` and `aomcopy.h` in the [src/libsoq](https://github.com/jleffler/soq/tree/master/src/libsoq) sub-directory. That's for an array of memory blocks rather than a linked list. – Jonathan Leffler Jul 03 '22 at 18:32

2 Answers2

1

If you are under gcc you can achieve that using statement expressions and nested functions, here an example of somtehing similar to a lambda.

In plain c is much more difficult and you would have to deal with NULL-terminated va_args lists and a lot of black magic. Another option is to use the preprocessor, also with its disadvantages, but at least the implementation is easier:

#define FOR_EACH(list, function, ...)                                            \
do {                                                                             \
    if (list == NULL || list->head == NULL) {                                    \
        break;                                                                   \
    }                                                                            \
    for (linked_list_node* node = list->head; node != NULL; node = node->next) { \
        function(node, __VA_ARGS__);                                             \
    }                                                                            \
} while (0)

void print(linked_list_node *node, int *count)
{
    printf("%d) %d\n", (*count)++, node->value);
}

void concat(linked_list_node *node, const char *str1, const char *str2)
{
    printf("%s %d %s\n", str1, node->value, str2);
}

int main(void)
{
    linked_list_node nodes[] = {{1, &nodes[1]} , {2, &nodes[2]}, {3, NULL}};
    linked_list *list = &(linked_list){3, &nodes[0], &nodes[2]};

    int count = 0;
    FOR_EACH(list, print, &count);
    FOR_EACH(list, concat, "Hi", "by");

    return 0;
}

Output:

0) 1
1) 2
2) 3
Hi 1 by
Hi 2 by
Hi 3 by
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
0

I didn't explain myself very well. I'm building a linked list library for C (just as a hobby), and I want that library to have a for_each function that mainly executes something (another function) to every single nodes in the linked list

Just let user pass an additional void * argument. See for example at fopencookie or pthread_create or thrd_create.

int for_each(linked_list* list, int (*function)(linked_list_node *, void *cookie), void *cookie) {
   FOREACH_YOUR_LIST(i, list) {
       int ret = function(i, cookie);
       if (ret) { 
          // let users do early return
          return ret;
       }
   }
   return 0;
}
    
KamilCuk
  • 120,984
  • 8
  • 59
  • 111