Skip to content

Instantly share code, notes, and snippets.

@pervognsen
Last active Nov 18, 2020
Embed
What would you like to do?
typedef struct {
// These fields are internal state and not considered part of the public interface.
Node *parent;
int next_index;
// This field is public and valid to read after a call to next that returns true.
Node *child;
} Iter;
Iter iter_children(Node *node) {
return (Iter){.parent = node};
}
bool iter_next(Iter *iter) {
Node *node = iter->parent;
switch (node->type) {
case MESH:
// Mesh nodes have no children
return false;
case TRANSFORM:
// Transform nodes have a variable number of children stored in an array.
if (iter->next_index < node->transform.num_children) {
iter->child = node->transform.children[iter->next_index++];
return true;
} else {
return false;
}
// ...
}
}
void print_node(Node *node, int level) {
print_indent(level);
switch (node->type) {
case MESH:
print_mesh(node->mesh);
break;
// ...
}
Iter iter = iter_children(node);
while (iter_next(&iter)) { // Or you can use (Iter iter = ...; iter_next(...);) { ... }
// When next returns true it means there's another item, and iter's public fields are updated and valid to read.
print_node(iter.child, level + 1);
}
// An iterator might own resources that need clean-up (e.g. a directory enumerator owns OS directory handles).
// I usually make it so that if you exhaust the sequence with next it cleans up after itself. But you need to support
// terminating iteration prematurely (like an early break), so you should expose an iter_finish(&iter) function which
// does clean-up explicitly and acts as a no-op if it's already cleaned up. That way the API user doesn't need to condition
// the iter_finish call on whether they terminated early or not.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment