When reading the blog, I saw these really long, complex bits of code and was thinking to myself, "_this_ is simpler?!"<p>It turns out those long complex pieces of code are not something the consumer, the program writer, me and you, will ever need to see. If the author reads this, I apologise for saying so, but IMO the blog post aims well but misfires: it needs to communicate to C++ developers what code _they_, ie _us_ normal folk, will see and write. Otherwise we'll get scared off. (But, I do note your absolute enthusiasm shines through and that carried me through the entire post :))<p>> At this point you may be wondering what’s the point to all of this. Senders and receivers, operation states with fiddly lifetime requirements, connect, start, three different callbacks — who wants to manage all of this? The C API was way simpler. It’s true! So why am I so unreasonably excited about all of this? The caller of async_read_file doesn’t need to care about any of that.<p>This is in "step 6", about ten screens into the blogpost. Good thing I read the whole thing!<p>So, THIS is the takeaway. You have old code (C-style, like the Win32 API):<p><pre><code> /// Old-style async C API with a callback
/// (like Win32's ReadFileEx)
struct overlapped {
// ...OS internals here...
};
using overlapped_callback =
void(int status, int bytes, overlapped* user);
int read_file(FILE*, char* buffer, int bytes,
overlapped* user, overlapped_callback* cb);
</code></pre>
and this transforms into new C++ code:<p><pre><code> exec::task< std::string > process_file( FILE* pfile )
{
std::string str;
str.resize( MAX_BUFFER );
auto [bytes, buff] =
co_await async_read_file(pfile, str.data(), str.size());
str.resize( bytes );
co_return str;
}
</code></pre>
Plus the additional money quote:<p>> You want to use senders because then you can stitch your async operations together with other operations from other libraries using generic algorithms from still other libraries. And so you can co_await your async operations in coroutines without having to write an additional line of code.<p>> Why do we need senders when C++ has coroutines? [...] this isn’t an either/or. Senders are part of the coroutine story.<p>And actually -- that looks pretty good to me!<p>And if the author reads this, if I may suggest, put this first! (Combined with the note about multiple different APIs all made cohesive, give two examples perhaps.) But sell the solution, then explain the intricate mechanism.<p>And, great work. I can see myself writing code using this. I like it.