Investigate replacing const_buffers_1 and mutable_buffers_1
With specialisations of std::begin() and std::end().
The constbuffersequence and mutablebuffersequence requirements would need to be updated in terms of begin() and end().
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);
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.
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.
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.
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::beginandstd::end - Remove the
const_buffers_1andmutable_buffers_1classes and instead provide specialisations ofstd::beginandstd::endforconst_bufferandmutable_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?