
Hi Christopher, --- christopher baus <christopher@baus.net> wrote: <snip>
I do think that Arkadily has a point. For better or worse, much of the programming world has been trained to think about sockets synchronously, and eventually boost/C++ should address this.
But the question is how this should be addressed :) I happen to think that the fact many people have been trained to think about sockets synchronously is most definitely for the worse. Of course there are situations where synchronous is more appropriate than asynchronous, but it's the lack of awareness of asynchronous as an alternative that can lead to inferior design choices.
When that happens it would be nice to use the same fundamental types presented in asio, and these users shouldn't have to know about demuxers.
With asio, I was hoping to make asynchronous operations as natural and easy to use as possible, compared to their synchronous counterparts. I think that providing a synchronous-only socket interface is counterproductive. It reinforces the notion that synchronous operations are somehow more useful for the ordinary programmer, and that asynchronous is hard and best left in the realm of the networking guru.
I just want to point out that async reading and writing could be a function of an async I/O demuxer and not the socket itself. The socket could be passed to the demuxer and not vice versa.
The above design is something I can fundamentally disagree with ;) That sort of design, in my opinion, relegates asynchronous operations to "second class citizens". Asynchronous operations are no less part of a socket interface than their synchronous counterparts. A write operation sends the same data on a socket whether it is performed synchronously or asynchronously. It's simply that a synchronous call blocks the calling thread until the operation completes, whereas an asynchronous call executes on a logical background thread and tells you when it is complete. Another goal of asio is to provide a basis for further abstraction. For this I believe it is important to provide a clear correlation between the synchronous and asynchronous operations. Consider a protocol where messages have a fixed length header followed by a body, where the body length is contained in the header. The synchronous read operation might look something like: void read_message(Stream& s, message& m) { ... create buffer for header ... read(s, header_buffer); ... create buffer for body of correct size ... read(s, body_buffer); ... populate message structure ... } The equivalent asynchronous code would be: void async_read_message(Stream& s, message& m, Handler h) { ... create buffer for header ... async_read(s, header_buffer, bind(header_handler ...)); } void header_handler(error e, Stream& s, message& m, Handler h) { ... check for failure ... ... create buffer for body of correct size ... async_read(s, body_buffer, bind(body_handler ...)); } void body_handler(error e, Stream& s, message& m, Handler h) { ... check for failure ... ... populate message structure ... h(e); // indicate that operation is complete } There is a relatively straightforward mapping between one and the other. In the future I want to exploit this mapping further by investigating the use of expression templates (perhaps similar to Boost.Lambda) to permit the encoding of a sequence of operations in a synchronous programming style. The operations could then be executed either synchronously or asynchronously as needed. Cheers, Chris