Boost logo

Boost :

From: Christopher Kohlhoff (chris_at_[hidden])
Date: 2005-10-17 21:09:35


Hi Beman,

Just addressing the void* buffers issue for now...

--- Beman Dawes <bdawes_at_[hidden]> 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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk