[libc++] Simplify std::ranges::subrange

Instead of using a base class to store the members and the optional
size, use [[no_unique_address]] to achieve the same thing without
needing a base class.

Also, as a fly-by:
- Change subrange from struct to class (per the standard)
- Improve the diagnostic for when one doesn't provide a size to the ctor of a sized subrange
- Replace this->member by just member since it's not in a dependent base anymore

This change would be an ABI break due to [[no_unique_address]], but we
haven't shipped ranges anywhere yet, so this shouldn't affect anyone.

Differential Revision: https://reviews.llvm.org/D110370
This commit is contained in:
Louis Dionne 2021-09-23 18:09:17 -04:00
parent 952f030fe6
commit 86df5a2fa8
2 changed files with 118 additions and 60 deletions

View File

@ -67,62 +67,41 @@ namespace ranges {
enum class _LIBCPP_ENUM_VIS subrange_kind : bool { unsized, sized };
template<class _Iter, class _Sent, bool>
struct __subrange_base {
static constexpr bool __store_size = false;
_Iter __begin_ = _Iter();
_Sent __end_ = _Sent();
_LIBCPP_HIDE_FROM_ABI
constexpr __subrange_base() = default;
_LIBCPP_HIDE_FROM_ABI
constexpr __subrange_base(_Iter __iter, _Sent __sent, make_unsigned_t<iter_difference_t<_Iter>> = 0)
: __begin_(_VSTD::move(__iter)), __end_(__sent) { }
};
template<class _Iter, class _Sent>
struct __subrange_base<_Iter, _Sent, true> {
static constexpr bool __store_size = true;
_Iter __begin_ = _Iter();
_Sent __end_ = _Sent();
make_unsigned_t<iter_difference_t<_Iter>> __size_ = 0;
_LIBCPP_HIDE_FROM_ABI
constexpr __subrange_base() = default;
_LIBCPP_HIDE_FROM_ABI
constexpr __subrange_base(_Iter __iter, _Sent __sent, decltype(__size_) __size)
: __begin_(_VSTD::move(__iter)), __end_(__sent), __size_(__size) { }
};
template<input_or_output_iterator _Iter, sentinel_for<_Iter> _Sent = _Iter,
subrange_kind _Kind = sized_sentinel_for<_Sent, _Iter>
? subrange_kind::sized
: subrange_kind::unsized>
requires (_Kind == subrange_kind::sized || !sized_sentinel_for<_Sent, _Iter>)
struct _LIBCPP_TEMPLATE_VIS subrange
: public view_interface<subrange<_Iter, _Sent, _Kind>>,
private __subrange_base<_Iter, _Sent, _Kind == subrange_kind::sized && !sized_sentinel_for<_Sent, _Iter>> {
using _Base = __subrange_base<_Iter, _Sent, _Kind == subrange_kind::sized && !sized_sentinel_for<_Sent, _Iter>>;
class _LIBCPP_TEMPLATE_VIS subrange
: public view_interface<subrange<_Iter, _Sent, _Kind>>
{
private:
static constexpr bool _StoreSize = (_Kind == subrange_kind::sized && !sized_sentinel_for<_Sent, _Iter>);
static constexpr bool _MustProvideSizeAtConstruction = !_StoreSize; // just to improve compiler diagnostics
struct _Empty { constexpr _Empty(auto) noexcept { } };
using _Size = conditional_t<_StoreSize, make_unsigned_t<iter_difference_t<_Iter>>, _Empty>;
[[no_unique_address]] _Iter __begin_ = _Iter();
[[no_unique_address]] _Sent __end_ = _Sent();
[[no_unique_address]] _Size __size_ = 0;
public:
_LIBCPP_HIDE_FROM_ABI
subrange() requires default_initializable<_Iter> = default;
_LIBCPP_HIDE_FROM_ABI
constexpr subrange(__convertible_to_non_slicing<_Iter> auto __iter, _Sent __sent)
requires (!_Base::__store_size)
: _Base(_VSTD::move(__iter), __sent) {}
requires _MustProvideSizeAtConstruction
: __begin_(_VSTD::move(__iter)), __end_(std::move(__sent))
{ }
_LIBCPP_HIDE_FROM_ABI
constexpr subrange(__convertible_to_non_slicing<_Iter> auto __iter, _Sent __sent,
make_unsigned_t<iter_difference_t<_Iter>> __n)
requires (_Kind == subrange_kind::sized)
: _Base(_VSTD::move(__iter), __sent, __n)
: __begin_(_VSTD::move(__iter)), __end_(std::move(__sent)), __size_(__n)
{
if constexpr (sized_sentinel_for<_Sent, _Iter>)
_LIBCPP_ASSERT((this->__end_ - this->__begin_) == static_cast<iter_difference_t<_Iter>>(__n),
_LIBCPP_ASSERT((__end_ - __begin_) == static_cast<iter_difference_t<_Iter>>(__n),
"std::ranges::subrange was passed an invalid size hint");
}
@ -132,8 +111,9 @@ namespace ranges {
convertible_to<sentinel_t<_Range>, _Sent>
_LIBCPP_HIDE_FROM_ABI
constexpr subrange(_Range&& __range)
requires (!_Base::__store_size)
: subrange(ranges::begin(__range), ranges::end(__range)) { }
requires (!_StoreSize)
: subrange(ranges::begin(__range), ranges::end(__range))
{ }
template<__different_from<subrange> _Range>
requires borrowed_range<_Range> &&
@ -141,9 +121,9 @@ namespace ranges {
convertible_to<sentinel_t<_Range>, _Sent>
_LIBCPP_HIDE_FROM_ABI
constexpr subrange(_Range&& __range)
requires _Base::__store_size && sized_range<_Range>
: subrange(__range, ranges::size(__range)) { }
requires _StoreSize && sized_range<_Range>
: subrange(__range, ranges::size(__range))
{ }
template<borrowed_range _Range>
requires __convertible_to_non_slicing<iterator_t<_Range>, _Iter> &&
@ -151,39 +131,47 @@ namespace ranges {
_LIBCPP_HIDE_FROM_ABI
constexpr subrange(_Range&& __range, make_unsigned_t<iter_difference_t<_Iter>> __n)
requires (_Kind == subrange_kind::sized)
: subrange(ranges::begin(__range), ranges::end(__range), __n) { }
: subrange(ranges::begin(__range), ranges::end(__range), __n)
{ }
template<__different_from<subrange> _Pair>
requires __pair_like_convertible_from<_Pair, const _Iter&, const _Sent&>
_LIBCPP_HIDE_FROM_ABI
constexpr operator _Pair() const { return _Pair(this->__begin_, this->__end_); }
constexpr operator _Pair() const {
return _Pair(__begin_, __end_);
}
_LIBCPP_HIDE_FROM_ABI
constexpr _Iter begin() const requires copyable<_Iter> {
return this->__begin_;
return __begin_;
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Iter begin() requires (!copyable<_Iter>) {
return _VSTD::move(this->__begin_);
return _VSTD::move(__begin_);
}
_LIBCPP_HIDE_FROM_ABI
constexpr _Sent end() const { return this->__end_; }
constexpr _Sent end() const {
return __end_;
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const { return this->__begin_ == this->__end_; }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const {
return __begin_ == __end_;
}
_LIBCPP_HIDE_FROM_ABI
constexpr make_unsigned_t<iter_difference_t<_Iter>> size() const
requires (_Kind == subrange_kind::sized)
{
if constexpr (_Base::__store_size)
return this->__size_;
if constexpr (_StoreSize)
return __size_;
else
return __to_unsigned_like(this->__end_ - this->__begin_);
return _VSTD::__to_unsigned_like(__end_ - __begin_);
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr subrange next(iter_difference_t<_Iter> __n = 1) const&
requires forward_iterator<_Iter> {
requires forward_iterator<_Iter>
{
auto __tmp = *this;
__tmp.advance(__n);
return __tmp;
@ -195,7 +183,8 @@ namespace ranges {
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr subrange prev(iter_difference_t<_Iter> __n = 1) const
requires bidirectional_iterator<_Iter> {
requires bidirectional_iterator<_Iter>
{
auto __tmp = *this;
__tmp.advance(-__n);
return __tmp;
@ -205,16 +194,16 @@ namespace ranges {
constexpr subrange& advance(iter_difference_t<_Iter> __n) {
if constexpr (bidirectional_iterator<_Iter>) {
if (__n < 0) {
ranges::advance(this->__begin_, __n);
if constexpr (_Base::__store_size)
this->__size_ += _VSTD::__to_unsigned_like(-__n);
ranges::advance(__begin_, __n);
if constexpr (_StoreSize)
__size_ += _VSTD::__to_unsigned_like(-__n);
return *this;
}
}
auto __d = __n - ranges::advance(this->__begin_, __n, this->__end_);
if constexpr (_Base::__store_size)
this->__size_ -= _VSTD::__to_unsigned_like(__d);
auto __d = __n - ranges::advance(__begin_, __n, __end_);
if constexpr (_StoreSize)
__size_ -= _VSTD::__to_unsigned_like(__d);
return *this;
}
};

View File

@ -0,0 +1,69 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-no-concepts
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr subrange() requires default_initializable<I>;
#include <ranges>
#include <cassert>
#include <cstddef>
#include "test_iterators.h"
// An input_or_output_iterator that is not default constructible so we can test
// the `requires` on subrange's default constructor.
struct NoDefaultIterator {
using difference_type = std::ptrdiff_t;
NoDefaultIterator() = delete;
NoDefaultIterator& operator++();
void operator++(int);
int& operator*() const;
friend bool operator==(NoDefaultIterator const&, NoDefaultIterator const&);
};
static_assert(std::input_or_output_iterator<NoDefaultIterator>);
// A sentinel type for the above iterator
struct Sentinel {
friend bool operator==(NoDefaultIterator const&, Sentinel const&);
friend bool operator==(Sentinel const&, NoDefaultIterator const&);
friend bool operator!=(NoDefaultIterator const&, Sentinel const&);
friend bool operator!=(Sentinel const&, NoDefaultIterator const&);
};
constexpr bool test() {
{
static_assert(!std::is_default_constructible_v<std::ranges::subrange<NoDefaultIterator, Sentinel, std::ranges::subrange_kind::sized>>);
static_assert(!std::is_default_constructible_v<std::ranges::subrange<NoDefaultIterator, Sentinel, std::ranges::subrange_kind::unsized>>);
}
{
using Iter = forward_iterator<int*>;
std::ranges::subrange<Iter, Iter, std::ranges::subrange_kind::sized> subrange;
assert(subrange.begin() == Iter());
assert(subrange.end() == Iter());
}
{
using Iter = forward_iterator<int*>;
std::ranges::subrange<Iter, Iter, std::ranges::subrange_kind::unsized> subrange;
assert(subrange.begin() == Iter());
assert(subrange.end() == Iter());
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}