Skip to content

Instantly share code, notes, and snippets.

@Prince781
Last active June 14, 2023 07:41
Show Gist options
  • Save Prince781/012ae4f6550db43c17b6cbc19c69d4a3 to your computer and use it in GitHub Desktop.
Save Prince781/012ae4f6550db43c17b6cbc19c69d4a3 to your computer and use it in GitHub Desktop.
mostly-typesafe iterator in C23
// ISO C23 iterator for a non-const container
// compile with -std=c2x (gcc 13+, clang 16+)
struct iterator_base {
void *collection;
union {
void *state;
unsigned long size;
};
unsigned long index;
bool has_next;
};
#define iterator(T) \
struct { \
struct iterator_base base; \
void (*iterate)(struct iterator_base *); \
T *(*get_item)(struct iterator_base *); \
}
#define const_iterator(T) \
struct { \
struct iterator_base base; \
void (*iterate)(struct iterator_base *); \
T const *(*get_item)(struct iterator_base *); \
}
#define iterator_init(it, expr) typeof(expr) it = expr
#define iterator_index(it) (it).base.index
#define iterator_has_next(it) (it).base.has_next
#define iterator_get_item(it) (it).get_item(&(it).base)
#define iterator_iterate(it) (it).iterate(&(it).base)
#define index_of(element) element ## _idx
#define for_each(element, iterable, statements) \
for (\
iterator_init(element ## _it, iterable); \
iterator_has_next(element ## _it); \
iterator_iterate(element ## _it) \
) { \
auto element = iterator_get_item(element ## _it); \
auto const index_of(element) = iterator_index(element ## _it); \
statements; \
}
// --------
#include <stdlib.h>
#include <assert.h>
static inline void *alloc0(size_t size) {
void *p = calloc(1, size);
assert(p && "failed to allocate!");
return p;
}
#define box(T) (T *) alloc0(sizeof(T))
// --------
#define list_node(T) \
struct { \
void *next; \
void *prev; \
T data; \
}
#define list(T) \
struct { \
list_node(T) *head; \
list_node(T) *tail; \
unsigned long length; \
T (*ref_element)(T); \
void (*unref_element)(T); \
union { \
iterator(T) dummy_it; \
const_iterator(T) dummy_const_it; \
}; \
}
#define list_empty(ls) ((ls)->length == 0)
#define list_append(ls, expr) \
do { \
auto ls_appending = (ls); \
if (!ls_appending->head) { \
ls_appending->head = box(typeof(*ls_appending->head)); \
ls_appending->head->data = ls_appending->ref_element ? ls_appending->ref_element(expr) : (expr); \
ls_appending->tail = (void *)ls_appending->head; \
} else { \
auto old_tail = ls_appending->tail; \
ls_appending->tail = box(typeof(*ls_appending->tail)); \
ls_appending->tail->data = ls_appending->ref_element ? ls_appending->ref_element(expr) : (expr); \
old_tail->next = ls_appending->tail; \
} \
ls_appending->length++; \
} while (0)
#define list_remove_head(ls) \
do { \
auto ls_removing_head = (ls); \
if (ls_removing_head->head) { \
auto node = ls_removing_head->head; \
if (ls_removing_head->unref_element) \
ls_removing_head->unref_element(node->data); \
if (node == (void *)ls_removing_head->tail) { \
ls_removing_head->head = nullptr; \
ls_removing_head->tail = nullptr; \
} else { \
ls_removing_head->head = node->next; \
ls_removing_head->head->prev = nullptr; \
} \
free(node); \
ls_removing_head->length--; \
} \
} while (0)
// create an iterator on the items
#define list_items(ls) \
((typeof((ls)->dummy_it)) { \
.base = { \
(ls), \
{ .state = (ls)->head }, \
.has_next = (ls)->head \
}, \
.iterate = list_items__iterate, \
.get_item = (typeof((ls)->dummy_it.get_item))list_items__get_item \
})
// create a constant iterator on the items
#define list_items_const(ls) \
((typeof((ls)->dummy_const_it)) { \
.base = { \
(ls), \
{ .state = (ls)->head }, \
.has_next = (ls)->head \
}, \
.iterate = list_items__iterate, \
.get_item = (typeof((ls)->dummy_const_it.get_item))list_items__get_item \
})
static void list_items__iterate(struct iterator_base *it)
{
// generic cast
list(void *) *dummy = it->collection;
list_node(void *) *node = it->state;
if (node != (void *)dummy->tail) {
it->state = node->next;
it->has_next = true;
it->index++;
} else
it->has_next = false;
}
static void *list_items__get_item(struct iterator_base *it)
{
list_node(void *) *node = it->state;
if (it->has_next)
return &node->data;
return nullptr;
}
#define list_destroy(ls) \
do { \
auto ls_destroying = (ls); \
while (ls_destroying->head) \
list_remove_head(ls_destroying); \
free(ls_destroying); \
} while (0)
// ---------------------------------
static void get_integer_stream__iterate(struct iterator_base *it)
{
if (it->index < it->size)
it->index++;
it->has_next = it->index < it->size;
}
static int *get_integer_stream__get_item(struct iterator_base *it)
{
return &((int *)it->collection)[it->index];
}
static iterator(int) get_integer_stream(int collection[], unsigned size) {
return (typeof(get_integer_stream(collection, size))) {
.base = {
collection,
{ .size = size },
.has_next = size
},
.iterate = get_integer_stream__iterate,
.get_item = get_integer_stream__get_item
};
}
static list(char const *) *get_sentence(void) {
auto ls = box(typeof(*get_sentence()));
list_append(ls, "so");
list_append(ls, "nice");
list_append(ls, "to");
list_append(ls, "meet");
list_append(ls, "you");
return ls;
}
#include <stdio.h>
// prints out:
// values:
// [0] = 1
// [1] = 2
// [2] = 3
// [3] = 4
// [4] = 5
// [5] = 6
// words:
// [0] = so
// [1] = nice
// [2] = to
// [3] = meet
// [4] = you
int main(void) {
int values[] = {1, 2, 3, 4, 5, 6};
printf("values: \n");
for_each(value_ref,
get_integer_stream(values, sizeof(values) / sizeof(values[0])),
printf(" [%lu] = %d\n", index_of(value_ref), *value_ref));
printf("words: \n");
auto words = get_sentence();
for_each(word_ref, list_items(words),
printf(" [%lu] = %s\n", index_of(word_ref), *word_ref));
// compile time error:
// for_each(word_ref, list_items_const(word), {
// *word_ref = "hello";
// printf(" [%lu] = %s\n", index_of(word_ref), *word_ref);
// });
list_destroy(words);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment