[libcxx-commits] [clang] [flang] [lld] [compiler-rt] [llvm] [clang-tools-extra] [libcxx] [libc++][ranges] P2116R9: Implements `views::enumerate` (PR #73617)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jan 3 19:10:55 PST 2024


================
@@ -0,0 +1,333 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___RANGES_ENUMERATE_VIEW_H
+#define _LIBCPP___RANGES_ENUMERATE_VIEW_H
+
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iterator_traits.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/maybe_const.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+#include <tuple>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+// [concept.object]
+
+template <class _Rp>
+concept __range_with_movable_references =
+    ranges::input_range<_Rp> && std::move_constructible<ranges::range_reference_t<_Rp>> &&
+    std::move_constructible<ranges::range_rvalue_reference_t<_Rp>>;
+
+// [range.enumerate.view]
+
+template <view _View>
+  requires __range_with_movable_references<_View>
+class enumerate_view : public view_interface<enumerate_view<_View>> {
+  _View __base_ = _View();
+
+  // [range.enumerate.iterator]
+  template <bool _Const>
+  class __iterator;
+
+  // [range.enumerate.sentinel]
+  template <bool _Const>
+  class __sentinel;
+
+  template <bool _AnyConst>
+  _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_current(const __iterator<_AnyConst>& __iter) {
+    return (__iter.__current_);
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr enumerate_view()
+    requires(default_initializable<_View>)
+  = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit enumerate_view(_View __base) : __base_(std::move(__base)){};
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>(ranges::begin(__base_), 0);
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires __range_with_movable_references<const _View>
+  {
+    return __iterator<true>(ranges::begin(__base_), 0);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    if constexpr (common_range<_View> && sized_range<_View>)
+      return __iterator<false>(ranges::end(__base_), ranges::distance(__base_));
+    else
+      return __sentinel<false>(ranges::end(__base_));
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires __range_with_movable_references<const _View>
+  {
+    if constexpr (common_range<const _View> && sized_range<const _View>)
+      return __iterator<true>(ranges::end(__base_), ranges::distance(__base_));
+    else
+      return __sentinel<true>(ranges::end(__base_));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return ranges::size(__base_);
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return ranges::size(__base_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+};
+
+template <class _Range>
+enumerate_view(_Range&&) -> enumerate_view<views::all_t<_Range>>;
+
+// [range.enumerate.iterator]
+
+template <view _View>
+  requires __range_with_movable_references<_View>
+template <bool _Const>
+class enumerate_view<_View>::__iterator {
+  using _Base = __maybe_const<_Const, _View>;
+
+  static consteval auto __get_iterator_concept() {
+    if constexpr (random_access_range<_Base>) {
+      return random_access_iterator_tag{};
+    } else if constexpr (bidirectional_range<_Base>) {
+      return bidirectional_iterator_tag{};
+    } else if constexpr (forward_range<_Base>) {
+      return forward_iterator_tag{};
+    } else {
+      return input_iterator_tag{};
+    }
+  }
+
+  friend class enumerate_view<_View>;
+
+public:
+  using iterator_category = input_iterator_tag;
+  using iterator_concept  = decltype(__get_iterator_concept());
+  using difference_type   = range_difference_t<_Base>;
+  using value_type        = tuple<difference_type, range_value_t<_Base>>;
+
+private:
+  using __reference_type       = tuple<difference_type, range_reference_t<_Base>>;
+  iterator_t<_Base> __current_ = iterator_t<_Base>();
+  difference_type __pos_       = 0;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current, difference_type __pos)
+      : __current_(std::move(__current)), __pos_(__pos) {}
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __iterator()
+    requires(default_initializable<iterator_t<_Base>>)
+  = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
+      : __current_(std::move(__i.__current_)), __pos_(__i.__pos_) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr difference_type index() const noexcept { return __pos_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const { return __reference_type(__pos_, *__current_); }
----------------
philnik777 wrote:

I guess we simply disagree whether this should be blocking. I don't see a good argument to be inconsistent now, since the options are that we (1) be inconsistent with the rest of the code base in this single case, or (2) you propose to extend `[[nodiscard]]` very soon and a patch applying it library-wide will be in the same release as `views::enumerate`. So there seems to be either a good reason to not add it or not a good reason to add it before the rest. The three people who adopt it within a few weeks are probably going to be fine dealing with an extra warning.

I just want to restate that I'm not against this change. I've pushed quite a bit to get more `[[nodiscard]]`s into libc++, and I'd even be in favour of adding `[[nodiscard]]`s (opt-in) to `operator++(int)` and friends. I'm purely against changing our policy quietly inside a PR.

https://github.com/llvm/llvm-project/pull/73617


More information about the libcxx-commits mailing list