Skip to content

Instantly share code, notes, and snippets.

Last active December 10, 2023 23:51
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save pervognsen/d57cdc165e79a21637fe5a721375afba to your computer and use it in GitHub Desktop.
This is a brief comment on this article on API design principles:
I've called that style of API a coroutine, iterator, state machine, push/pull or client/server API
depending on what seems most appropriate for the context, but they are all variants of the same idea.
It's particularly superior in many cases as an alternative to callback-based APIs, which are the more
common way of providing this kind of fine-grained interleaving between library code and user code.
Callbacks can still be superior in usability for context-free operations like memory allocation,
especially in languages with first-class functions, but when you want interlocked execution between
user code and library code, with user code in charge, the inversion of control is a major problem.
While this is in the realm of personal taste and opinion, I might write the API slightly differently:
struct unzip_t unzip = { ... };
struct unzip_packet_t packet;
while (unzip_next(&unzip, &packet)) {
if (packet.type == UNZIP_FILE) {
packet.file.address = zip_file_memory + packet.file.offset;
} else if (packet.type == UNZIP_TOC) {
packet.toc.address = toc = malloc(packet.toc.size);
return unzip.status != UNZIP_ERROR;
This uses the model of a client/server API and the conceit that a reply is carried in the same envelope as the request
or maybe that the request is like a paper form with certain fields that the receiver fills out in place and returns
to the sender. With that packet overwriting approach, it's important that unzip_next sets the mandatory packet reply
fields with sentinel values so it can detect unfilled fields and crash loudly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment