This is a brief comment on this article on API design principles: | |
https://gist.github.com/vurtun/192cac1f1818417d7b4067d60e4fe921 | |
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 = { ... }; | |
unzip_init(&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) { | |
free(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