Last active
June 14, 2023 07:41
-
-
Save Prince781/012ae4f6550db43c17b6cbc19c69d4a3 to your computer and use it in GitHub Desktop.
mostly-typesafe iterator in C23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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