MutableBufferSequence

A MutableBufferSequence represents zero or more contiguous writable memory regions as a bidirectional range whose value type is convertible to mutable_buffer, or as an object which is convertible to mutable_buffer.

All mutable buffer sequences are also constant buffer sequences, since writable memory can always be read.

Concept Definition

template<typename T>
concept mutable_buffer_sequence =
    std::is_convertible_v<T, mutable_buffer> || (
        std::ranges::bidirectional_range<T> &&
        std::is_convertible_v<std::ranges::range_value_t<T>, mutable_buffer>);

Requirements

  • T denotes a type meeting the MutableBufferSequence requirements.

  • t denotes a value of type T.

  • u denotes a value of type T.

Expression Type Semantics, Pre/Post-conditions

mutable_buffer_sequence<T>

bool

true if T models MutableBufferSequence.

buffers::begin(t)

Iterator

Returns a bidirectional iterator to the first buffer, or buffers::end(t) if the sequence is empty.

buffers::end(t)

Iterator

Returns a bidirectional iterator one past the last buffer.

*it

Convertible to mutable_buffer

Where it is an iterator obtained from buffers::begin(t).

T u(t)

Copy construction. u must reference the same memory regions as t.

Iteration

While std::ranges::begin and std::ranges::end work on bidirectional ranges, they do not handle objects merely convertible to mutable_buffer. Use buffers::begin and buffers::end for uniform handling:

template<class MutableBufferSequence>
std::size_t fill_with_zeros(MutableBufferSequence const& bs)
{
    std::size_t total = 0;
    for(auto it = buffers::begin(bs); it != buffers::end(bs); ++it)
    {
        mutable_buffer b = *it;
        std::memset(b.data(), 0, b.size());
        total += b.size();
    }
    return total;
}

// Works with ranges
std::vector<mutable_buffer> v = /* ... */;
fill_with_zeros(v);

// Also works with single buffers
char data[100];
mutable_buffer mb(data, sizeof(data));
fill_with_zeros(mb);

Copy Semantics

Copies of a buffer sequence must reference the same underlying memory:

T t(u);
static_assert(mutable_buffer_sequence<T>);
assert(std::equal(
    buffers::begin(t), buffers::end(t),
    buffers::begin(u), buffers::end(u),
    [](mutable_buffer const& b1, mutable_buffer const& b2)
    {
        return b1.data() == b2.data() && b1.size() == b2.size();
    }));

This property enables efficient pass-by-value semantics. Copying a buffer sequence is cheap because only the handle is copied, not the underlying data.

Relationship to ConstBufferSequence

Every MutableBufferSequence is also a ConstBufferSequence. This allows mutable buffer sequences to be used wherever constant buffer sequences are expected:

template<class ConstBufferSequence>
std::size_t count_bytes(ConstBufferSequence const& bs);

char data[100];
mutable_buffer mb(data, sizeof(data));

// mutable_buffer works with ConstBufferSequence algorithms
std::size_t n = count_bytes(mb);

Models

The following types model MutableBufferSequence:

See Also