asio-tr2 icon indicating copy to clipboard operation
asio-tr2 copied to clipboard

Investigate replacing const_buffers_1 and mutable_buffers_1

Open chriskohlhoff opened this issue 10 years ago • 5 comments

With specialisations of std::begin() and std::end().

The constbuffersequence and mutablebuffersequence requirements would need to be updated in terms of begin() and end().

chriskohlhoff avatar Feb 26 '15 08:02 chriskohlhoff

The use of std::begin() and std::end() in the requirements does not seem to be enough to allow arrays to be used directly as buffer sequences.

#include <iostream>

template <class Sequence>
struct send_op
{
  Sequence sequence;

  void do_send()
  {
    std::cout << sizeof(sequence) << std::endl;
  }
};

template <class Sequence>
void send(const Sequence& sequence)
{
  std::cout << sizeof(sequence) << std::endl;

  send_op<Sequence> op{sequence};
  op.do_send();
}

int main()
{
  int arr[100];
  send(arr);
}

fails with compiler error:

copyarray.cpp:19:24: error: cannot initialize an array element of type 'int' with an lvalue of type 'int const[100]'
  send_op<Sequence> op{sequence};
                       ^~~~~~~~
copyarray.cpp:26:3: note: in instantiation of function template specialization 'send<int [100]>' requested here
  send(arr);

chriskohlhoff avatar Feb 27 '15 04:02 chriskohlhoff

I was thinking about this today, and I realized that std::initializer_list<const buffer> fulfills all the requirements of a const_buffer_sequence. You might have to add another overload in a few places, but then people could write {buf} instead of const_buffer_1{buf} to get a sequence.

mclow avatar Mar 17 '15 20:03 mclow

It does fulfil the syntactic requirements, but I suspect it's likely to be error prone when used with async operations. According to [support.initlist] an initializer_list does not copy the underlying elements, which means this:

async_read(socket, {buffer(my_vec)}, handler);

may crash as soon as async_read() returns and the async operation is pending.

chriskohlhoff avatar Mar 17 '15 20:03 chriskohlhoff

There are definitely lifetime issues. I suspect they are surmountable, especially given the fact that const_buffer, etc are cheap to copy. That doesn't mean that this is a good idea, but I thought it was worth exploring.

If you think it's not worth doing, that's fine.

mclow avatar Mar 18 '15 04:03 mclow

Pre-Lenexa Summary

[buffer.const.1], [buffer.mutable.1]

The const_buffers_1 and mutable_buffers_1 classes adapt the const_buffer and mutable_buffer respectively to the buffer sequence requirements. I.e. they represent buffer sequences of exactly one buffer -- the most common case. In the TR2 era I was thinking they might have been replaced by a concept map.

In Cologne there was some dislike of the public non-virtual inheritance. The proposal was that we could:

  • Change the ConstBufferSequence and MutabeBufferSequence to be specified in terms of std::begin and std::end
  • Remove the const_buffers_1 and mutable_buffers_1 classes and instead provide specialisations of std::begin and std::end for const_buffer and mutable_buffer.

For example:

namespace std {
  const_buffer* begin(const_buffer& b) { return std::addressof(b); }
  const_buffer* end(const_buffer& b) { return std::addressof(b) + 1; }
  // etc.
}

Is it ok to do this?

chriskohlhoff avatar May 05 '15 10:05 chriskohlhoff