
Hi Beman, --- Beman Dawes <bdawes@acm.org> wrote:
There are still some void *'s in the public interface. The io_control
helpers have data() members returning void *'s, for example.
Any chance of wringing out the non-memory management void *'s before a submission? I know it sounds picky, but a lot of C++ programmers object strongly to void * in public interfaces for anything except the rawest of raw memory management.
Hmm, I'm not sure. Here are the current uses of void* and their rationales: * buffer() and buffers() - so that arbitrary application data structures can be sent without an additional buffer copy. * const_buffer::data() and mutable_data::data() - to get a pointer to pass to OS functions like send and recv. * IO_Control_Command::data() - to get a pointer to pass to OS functions like ioctl or WSAIOCtl. * Socket_Option::data() - to get a pointer to be pass to OS functions like setsocketopt and getsockopt. The last 3 are similar, in that they involve getting a pointer to be passed to a low-level OS function. (Note that the Endpoint concept has a similar thing, except that it returns an implementation-defined type rather than void*.) I don't want to preclude user-defined IO_Control_Command or Socket_Option types, so there has to be *some* way of getting a pointer to the data in the public interface. However I'd be very happy to change it if there's a better way of accomplishing the same thing.
I also wonder if the interface could be thinned without reducing functionality. For example, could buffer() and buffers() be folded into one function, or at least overloads with the same name.
I had considered making the mutable_buffer/const_buffer classes also implement the Mutable_Buffers/Const_Buffers concepts, but i found it was confusing as to whether the begin()/end() functions applied to the underlying memory. What about this for an idea: - Remove the buffer() functions. - Rename buffers() to buffer(). - Have a specialisation for const_buffer<1> that supports conversion to const_buffer. - Have a specialisation for mutable_buffer<1> that supports conversion to mutable_buffer and const_buffer. Then to send a single buffer you could write: sock.write(buffer(data, size)); and still use the chaining to send multiple buffers: sock.write(buffer(data1, size1)(data2, size2)); The conversion to the individual buffer classes should still let you write: std::vector<const_buffer> bufs; bufs.push_back(buffer(data1, size1)); bufs.push_back(buffer(data2, size2)); sock.write(bufs);' or: const_buffers<2> bufs = { buffer(data1, size1), buffer(data2, size2) }; sock.write(bufs);
Likewise, could the 12 free read/write functions be reduced to 2 names (presumably read/write) or 4 names (presumably read, async_read, write, async_write) via folding and/or overloading?. Am I the only one who feels the large number of names makes the interface appear more complex than it really is?
No you're not, especially after I've had to type async_read_at_least_n many times ;) Reducing to 4 names might be feasible. One thing I realised recently is that, in terms of behaviour, read() and read_n() are just special cases of read_at_least_n(). The same obviously applies to write*() and the async equivalents. Could something be done by extending the Mutable_Buffers/Const_Buffers concepts to have a desired_minimum_transfer() member function? Bad name, I know, but it would return the minimum number of bytes to read or write from the list of buffers. It would have to be optional so that containers like std::vector can still meet the concept requirements. It would probably have a default of 0, i.e. same semantics as current asio::read() or asio::write(). You could then write: read(sock, bufs); // Same as existing read() read(sock, all_of(bufs)); // Same as read_n() read(sock, at_least(bufs, 42)); // Same as read_at_least_n(). I'm ready to take guidance here :) Cheers, Chris