Поскольку вы пометили этот C11, вы можете использовать _Generic
для этой цели. _Generic
хорошо, так как он безопасен от типа, в отличие от старой школы void*
, что довольно опасно. Это также решает проблему отсутствия общего эквивалента void*
, который можно использовать с типами указателей на функции.
У вас может быть функция «функтор», которая вызывается для каждого элемента в контейнере, например:
typedef void node_operation_t (size_t index, node_t* node);
// functions following this form:
void node_add (size_t index, node_t* node);
void node_delete (size_t index, node_t* node);
...
Тогда вы хотите назвать это как
linked_list_t list;
traverse(&list, node_delete);
(Именование: "traverse" - это часто используемый термин для общей итерации контейнера в C, тогда как "foreach" - это скорее ключевое слово цикла в различных других языках.)
Теперь вы можете создать для этого макрос _Generic
, принимая либо связанные списки, либо другие контейнеры:
#define traverse(list, func) \
_Generic((func), \
node_operation_t*: traverse_linked_list, \
foo_operation_t*: traverse_foo \
)(list, func)
Выбирает соответствующую функцию, проверяя тип указателя на функцию. Это необходимо, только если разные функции относятся к разным типам, в противном случае вы могли бы также сделать traverse
функцией вместо этого.
Аналогичным образом можно вместо этого иметь несколько операций с разными входами параметров, работающих с одним и тем же типом контейнера. Много возможностей.
traverse_linked_list
здесь было бы что-то вроде:
void traverse_linked_list (linked_list_t* ll, node_operation_t* op)
, который просто перебирает список и вызывает op
для каждого узла. Он может быть расширен для получения большего количества параметров, если вы хотите передать параметры от вызывающей стороны до каждой отдельной операции (например, «установить для всех элементов значение 5»).