2779. [networking.ts] Relax requirements on buffer sequence iterators

Section: 99 [networking.ts::buffer.reqmts.mutablebuffersequence] Status: WP Submitter: Vinnie Falco Opened: 2016-10-05 Last modified: 2017-11-13

Priority: Not Prioritized

View all issues with WP status.

Discussion:

Addresses: networking.ts

We propose to relax the ForwardIterator requirements of buffer sequences in [networking.ts] by allowing buffer sequence iterators to return rvalues when dereferenced, and skip providing operator->. A paraphrased explanation of why the referential equality rules of ForwardIterator are harmful to the buffer sequence requirements comes from N4128, 3.3.7 "Ranges For The Standard Library":

The [networking.ts] dependence on ForwardIterator in the buffer sequence requirements ties together the traversal and access properties of iterators. For instance, no forward iterator may return an rvalue proxy when it is dereferenced; the ForwardIterator concept requires that unary operator* return an lvalue. This problem has serious consequences for lazy evaluation that applies transformations to buffer sequence elements on the fly. If the transformation function does not return an lvalue, the range's iterator can model no concept stronger than InputIterator, even if the resulting iterator could in theory support BidirectionalIterator. The result in practice is that most range adaptors today will not be compatible with [networking.ts], thereby limiting the types that [networking.ts] can be passed, for no good reason.

Consider a user defined function trim which lazily adapts a ConstBufferSequence, such that when iterating the buffers in the new sequence, each buffer appears one byte shorter than in the underlying sequence:

#include <boost/range/adaptor/transformed.hpp>

struct trim
{
  using result_type = const_buffer;
  result_type operator()(const_buffer b)
  {
    return const_buffer{b.data(), b.size() - 1};
  }
};

template <ConstBufferSequence>
auto
trim(ConstBufferSequence const& buffers)
{
  using namespace boost::adaptors;
  return buffers | transformed(trim{});
}

trim returns a BidirectionalRange, whose const_iterator returns an rvalue when dereferenced. This breaks the requirements of ForwardIterator. A solution that meets the referential equality rules of ForwardIterator, would be to evaluate the transformed sequence upon construction (for example, by storing each transformed const_buffer in a vector). Unfortunately this work-around is more expensive since it would add heap allocation which the original example avoids.

The requirement of InputIterator operator-> is also unnecessary for buffer sequence iterators, and should be removed. Because [networking.ts] only requires that a buffer sequence iterator's value_type be convertible to const_buffer or mutable_buffer, implementations of [networking.ts] cannot assume the existence of any particular member functions or data members other than an implicit conversion to const_buffer or mutable_buffer. Removing the requirement for operator-> to be present, provides additional relief from the referential equality requirements of ForwardIterator and allows transformations of buffer sequences to meet the requirements of buffer sequences.

This proposal imposes no changes on existing implementations of [networking.ts]. It does not change anything in the standard. The proposal is precise, minimal, and allows range adapters to transform buffer sequences in optimized, compatible ways.

[Issues processing Telecon 2016-10-07]

Status set to LEWG

[2017-02-21, Jonathan comments]

The use of the term "strict aliasing" in the issue discussion is misleading as that refers to type-based alias analysis in compilers, but the rule for ForwardIterators is related to referential equality and not strict aliasing.

[2017-02-22, Vinnie Falco comments]

We have eliminated the use of the term "strict aliasing" from the discussion.

[2017-07-10, Toronto, LEWG comments]

Status change: LEWG → Open.

Forward to LWG with the note that they may want to use "input +" instead of "bidirectional -". Unanimous yes.

[2017-07 Toronto Wednesday night issue processing]

Status to Ready

Proposed resolution:

This wording is relative to N4588.

  1. Modify 16.2.1 [buffer.reqmts.mutablebuffersequence] as indicated:

    An iterator type meeting the requirements for bidirectional iterators (C++Std [bidirectional.iterators]) whose value type is convertible to mutable_buffer

    An iterator type whose reference type is convertible to mutable_buffer, and which satisfies all the requirements for bidirectional iterators (C++Std [bidirectional.iterators]) except that:

    1. there is no requirement that operator-> is provided, and
    2. there is no requirement that reference be a reference type.
  2. Modify 16.2.2 [buffer.reqmts.constbuffersequence] as indicated:

    An iterator type meeting the requirements for bidirectional iterators (C++Std [bidirectional.iterators]) whose value type is convertible to const_buffer.

    An iterator type whose reference type is convertible to const_buffer, and which satisfies all the requirements for bidirectional iterators (C++Std [bidirectional.iterators]) except that:

    1. there is no requirement that operator-> is provided, and
    2. there is no requirement that reference be a reference type.