
Hi Beman, Just addressing the void* buffers issue for now... --- Beman Dawes <bdawes@acm.org> wrote:
Where did this idea come from that the only way to avoid an additional copy is to use a void *?
It's not just avoiding a copy, it's avoiding a copy *and* allowing arbitrary data structures to be sent. IMHO an important use case for C++ and networking is being able to do things like: struct message { int a; double b; int c_size; char c[0]; }; message* m = ...; sock.write(buffers(m, sizeof(message) + m->c_size)); Additionally, we must consider the fact that socket operations are byte-oriented and that they can, and usually do, result in fewer bytes being transferred than was requested. If we do not allow an interface that can address arbitrary memory, what happens in a situation like this: double values[100]; size_t bytes_read = read(sock, buffers(values)); // bytes_read == 17, what now?
Think about the iterator interfaces to Standard Library algorithms. They traffic nicely in pointers, yet not a buffer copy or void * to be seen.
So instead of:
... foo( void * data, size_t size);
something like:
template <class RandomAccessIterator> ... foo( RandomAccessIterator first, RandomAccessIterator last );
The RandomAccessIterator concept, as an example, is not strict enough for a networking use case, Unlike STL algorithms, we must consider that the underlying OS primitives deal in contiguous memory with a granularity of bytes. So, I firmly believe that a representation of raw memory is essential. The mutable_buffer and const_buffer classes are trying to provide a "safe" representation of this concept. They already address the issue of buffer overrun protection. And I do feel that the buffers classes (i.e. address & size) represent the restricted concept of a contiguous memory range better than iterators. If the issue here is preventing violations of type safety then how about I make conversion into a mutable_buffer/const_buffer a (for the most part) one-way operation. Or put another way, the buffer is in a sense typeless. That is, it is relatively easy to create a buffer object that represents some memory, but harder to go back the other way. In practice this means removing the void* data() member function from the buffer classes, and replacing it with a template <class T> T data_cast(...) as you suggest (although I might call it buffer_cast). The asio internals will use buffer_cast<void*>(buf) to obtain the pointer to be passed to send() or recv(). The buffer(void*, size_t) overload would be retained however, but since this is a conversion *to* a "typeless" buffer, I don't see this is a type safety violation in itself. You would have to use buffer_cast to violate type safety now. Cheers, Chris