The macro should be called <i>select</i>, since that's what Go calls it. (Of course I know there is a POSIX function (not C) by that name; so what).<p>This "choose {" "end }" business is an awful way to design the macro. If you need nesting that involves a block, with prolog and epilog material on either or both ends, you just have to hide that into the macro:<p><pre><code> selbegin;
out(ch, int, 42):
foo();
in(ch, int, i):
bar(i);
otherwise:
baz();
selend;
</code></pre>
I don't understand the downvotes. I have 30 years of (continous!) C experience. This is the best practice for doing a macro like this.<p>This "end }" business has improper nesting, visually. The <i>choose</i> is outside of the braces, the <i>end</i> is within; it is scatter-brained. There is no error checking for a forgotten <i>end</i>.<p>I'm looking at the implementation and it's looking quite weird! <i>choose</i> simply expands into <i>mill_choose_init__</i>, and <i>end</i> expands into <i>mill_choose_end__</i>. The definitions of these seem as if they should pair together:<p><pre><code> #define mill_choose_init__ \
{\
mill_choose_init_(MILL_HERE_);\
int mill_idx = -2;\
while(1) {\
if(mill_idx != -2) {\
if(0)
#define mill_choose_end__ \
break;\
}\
}\
mill_idx = mill_choose_wait_();\
}
</code></pre>
But, oops, look at the obvious lack of balance in the braces.<p><pre><code> mill_choose_init__ {
mill_choose_end__ }
</code></pre>
we are putting a brace after the if (0) which balances the one after break, and the final missing brace that balances the one opened internally in mill_choose_init__.<p>There are best practices to design this sort of macrology that don't require the user put braces in weird places to balance the inconsistencies in macro expansions.<p>Speaking as a professional, I would not pass this in a code review.<p>That we have to use a flaky preprocessor in order to get a half decent syntax is already a significant regret. We must not compound the regret by allowing the macrology to be less than as good as it can be.
This is something that would be extremely useful on microcontrollers, where you do lots of event-driven programming. Any sane design ends up with a state machine (or a hierarchy of state machines) driven by events generated in interrupts.<p>Coming from Clojure, I wished for something like core.async on microcontrollers for a long time — a way to convert most of my state machine into sequential code, with the complexity hidden, all while keeping everything in C (converted/generated during compile).
There's was an episode of the This Week In Tech podcast from 2015 that interviewed LibMill's creator about its design concepts:<p><a href="https://twit.tv/shows/floss-weekly/episodes/358" rel="nofollow">https://twit.tv/shows/floss-weekly/episodes/358</a><p>And HN discussion from 4 years ago:
<a href="https://news.ycombinator.com/item?id=10585505" rel="nofollow">https://news.ycombinator.com/item?id=10585505</a>
It seems like development stopped a few years ago. There were steady releases almost monthly for a few years and then it just stops in 2017. Did the author lose interest?<p>Are there any plans to support multicore? I noticed one of the examples addresses this by using fork(). Multicore is something goroutines > 1.5 support.
I actually really like the syntax for how they're emulating sending and receiving from channels. I like it better than Go's way of doing this with the <- operator.<p>What would be nice would be a defer function for closing channels and things.