[libcxx-commits] [libcxx] 40d6d2c - [libcxx][ranges] Add `ranges::iter_swap`.

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jun 22 09:53:10 PDT 2021


Author: zoecarver
Date: 2021-06-22T09:52:40-07:00
New Revision: 40d6d2c49dd16641a6fd40916e1cc9a58513ed15

URL: https://github.com/llvm/llvm-project/commit/40d6d2c49dd16641a6fd40916e1cc9a58513ed15
DIFF: https://github.com/llvm/llvm-project/commit/40d6d2c49dd16641a6fd40916e1cc9a58513ed15.diff

LOG: [libcxx][ranges] Add `ranges::iter_swap`.

Differential Revision: https://reviews.llvm.org/D102809

Added: 
    libcxx/include/__iterator/iter_swap.h
    libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap.pass.cpp

Modified: 
    libcxx/include/CMakeLists.txt
    libcxx/include/iterator
    libcxx/include/module.modulemap
    libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp
    libcxx/test/std/iterators/iterator.requirements/iterator.cust/unqualified_lookup_wrapper.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 167c06b105deb..75e0efc34b2dc 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -111,6 +111,7 @@ set(files
   __iterator/default_sentinel.h
   __iterator/incrementable_traits.h
   __iterator/iter_move.h
+  __iterator/iter_swap.h
   __iterator/iterator_traits.h
   __iterator/next.h
   __iterator/prev.h

diff  --git a/libcxx/include/__iterator/iter_swap.h b/libcxx/include/__iterator/iter_swap.h
new file mode 100644
index 0000000000000..a529472e2a13d
--- /dev/null
+++ b/libcxx/include/__iterator/iter_swap.h
@@ -0,0 +1,94 @@
+// -*- 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___ITERATOR_ITER_SWAP_H
+#define _LIBCPP___ITERATOR_ITER_SWAP_H
+
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/readable_traits.h>
+#include <__ranges/access.h>
+#include <concepts>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if !defined(_LIBCPP_HAS_NO_RANGES)
+
+namespace ranges {
+namespace __iter_swap {
+  template<class _I1, class _I2>
+  void iter_swap(_I1, _I2) = delete;
+
+  template<class _T1, class _T2>
+  concept __unqualified_iter_swap = requires(_T1&& __x, _T2&& __y) {
+    iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y));
+  };
+
+  template<class _T1, class _T2>
+  concept __readable_swappable =
+    indirectly_readable<_T1> && indirectly_readable<_T2> &&
+    swappable_with<iter_reference_t<_T1>, iter_reference_t<_T2>>;
+
+  struct __fn {
+    template <class _T1, class _T2>
+      requires __unqualified_iter_swap<_T1, _T2>
+    constexpr void operator()(_T1&& __x, _T2&& __y) const
+      noexcept(noexcept(iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y))))
+    {
+      (void)iter_swap(_VSTD::forward<_T1>(__x), _VSTD::forward<_T2>(__y));
+    }
+
+    template <class _T1, class _T2>
+      requires (!__unqualified_iter_swap<_T1, _T2>) &&
+               __readable_swappable<_T1, _T2>
+    constexpr void operator()(_T1&& __x, _T2&& __y) const
+      noexcept(noexcept(ranges::swap(*_VSTD::forward<_T1>(__x), *_VSTD::forward<_T2>(__y))))
+    {
+      ranges::swap(*_VSTD::forward<_T1>(__x), *_VSTD::forward<_T2>(__y));
+    }
+
+    template <class _T1, class _T2>
+      requires (!__unqualified_iter_swap<_T1, _T2> &&
+                !__readable_swappable<_T1, _T2>) &&
+               indirectly_movable_storable<_T1, _T2> &&
+               indirectly_movable_storable<_T2, _T1>
+    constexpr void operator()(_T1&& __x, _T2&& __y) const
+      noexcept(noexcept(iter_value_t<_T2>(ranges::iter_move(__y))) &&
+               noexcept(*__y = ranges::iter_move(__x)) &&
+               noexcept(*_VSTD::forward<_T1>(__x) = declval<iter_value_t<_T2>>()))
+    {
+      iter_value_t<_T2> __old(ranges::iter_move(__y));
+      *__y = ranges::iter_move(__x);
+      *_VSTD::forward<_T1>(__x) = _VSTD::move(__old);
+    }
+  };
+} // end namespace __iter_swap
+
+inline namespace __cpo {
+  inline constexpr auto iter_swap = __iter_swap::__fn{};
+} // namespace __cpo
+
+} // namespace ranges
+
+#endif // !defined(_LIBCPP_HAS_NO_RANGES)
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___ITERATOR_ITER_SWAP_H

diff  --git a/libcxx/include/iterator b/libcxx/include/iterator
index 929cc164384eb..0b5c4eedca180 100644
--- a/libcxx/include/iterator
+++ b/libcxx/include/iterator
@@ -562,6 +562,7 @@ template <class E> constexpr const E* data(initializer_list<E> il) noexcept;
 #include <__iterator/default_sentinel.h>
 #include <__iterator/incrementable_traits.h>
 #include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
 #include <__iterator/prev.h>

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index a49769b758dcc..de3daf8faf0c5 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -455,6 +455,7 @@ module std [system] {
       module default_sentinel     { header "__iterator/default_sentinel.h"     }
       module incrementable_traits { header "__iterator/incrementable_traits.h" }
       module iter_move            { header "__iterator/iter_move.h"            }
+      module iter_swap            { header "__iterator/iter_swap.h"            }
       module iterator_traits      { header "__iterator/iterator_traits.h"      }
       module next                 { header "__iterator/next.h"                 }
       module prev                 { header "__iterator/prev.h"                 }

diff  --git a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp
index 4f5d0eb7e1829..cf80bdeb205ba 100644
--- a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_move.pass.cpp
@@ -50,27 +50,6 @@ class iterator_wrapper {
   I base_ = I{};
 };
 
-class move_tracker {
-public:
-  move_tracker() = default;
-
-  constexpr move_tracker(move_tracker&& other) noexcept : moves_{other.moves_ + 1} { other.moves_ = 0; }
-
-  constexpr move_tracker& operator=(move_tracker&& other) noexcept {
-    moves_ = other.moves_ + 1;
-    other.moves_ = 0;
-    return *this;
-  }
-
-  constexpr move_tracker(move_tracker const& other) = delete;
-  constexpr move_tracker& operator=(move_tracker const& other) = delete;
-
-  [[nodiscard]] constexpr int moves() const noexcept { return moves_; }
-
-private:
-  int moves_ = 0;
-};
-
 template <typename I>
 constexpr void unqualified_lookup_move(I first_, I last_, I result_first_, I result_last_) {
   auto first = ::check_unqualified_lookup::unqualified_lookup_wrapper{std::move(first_)};

diff  --git a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap.pass.cpp
new file mode 100644
index 0000000000000..73f0e306fd1c6
--- /dev/null
+++ b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap.pass.cpp
@@ -0,0 +1,209 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: gcc-10
+
+// template<class I>
+// unspecified iter_swap;
+
+#include <iterator>
+
+#include <array>
+#include <cassert>
+
+#include "./unqualified_lookup_wrapper.h"
+#include "test_iterators.h"
+
+using IterSwapT = decltype(std::ranges::iter_swap);
+
+static_assert(std::semiregular<std::remove_cv_t<IterSwapT>>);
+
+struct HasIterSwap {
+  int &value_;
+  explicit HasIterSwap(int &value) : value_(value) { assert(value == 0); }
+
+  friend constexpr void iter_swap(HasIterSwap& a, HasIterSwap& b) {
+    a.value_ = 1;
+    b.value_ = 1;
+  }
+  friend constexpr void iter_swap(HasIterSwap& a, int& b) {
+    a.value_ = 2;
+    b = 2;
+  }
+};
+
+static_assert( std::is_invocable_v<IterSwapT, HasIterSwap&, HasIterSwap&>);
+static_assert( std::is_invocable_v<IterSwapT, HasIterSwap&, int&>);
+static_assert(!std::is_invocable_v<IterSwapT, int&, HasIterSwap&>);
+
+static_assert( std::is_invocable_v<IterSwapT&, HasIterSwap&, HasIterSwap&>);
+static_assert( std::is_invocable_v<IterSwapT&, HasIterSwap&, int&>);
+static_assert(!std::is_invocable_v<IterSwapT&, int&, HasIterSwap&>);
+
+static_assert( std::is_invocable_v<IterSwapT&&, HasIterSwap&, HasIterSwap&>);
+static_assert( std::is_invocable_v<IterSwapT&&, HasIterSwap&, int&>);
+static_assert(!std::is_invocable_v<IterSwapT&&, int&, HasIterSwap&>);
+
+struct NodiscardIterSwap {
+  [[nodiscard]] friend int iter_swap(NodiscardIterSwap&, NodiscardIterSwap&) { return 0; }
+};
+
+void ensureVoidCast(NodiscardIterSwap& a, NodiscardIterSwap& b) { std::ranges::iter_swap(a, b); }
+
+struct HasRangesSwap {
+  int &value_;
+  explicit HasRangesSwap(int &value) : value_(value) { assert(value == 0); }
+
+  friend constexpr void swap(HasRangesSwap& a, HasRangesSwap& b) {
+    a.value_ = 1;
+    b.value_ = 1;
+  }
+  friend constexpr void swap(HasRangesSwap& a, int& b) {
+    a.value_ = 2;
+    b = 2;
+  }
+};
+
+struct HasRangesSwapWrapper {
+  using value_type = HasRangesSwap;
+
+  HasRangesSwap &value_;
+  explicit HasRangesSwapWrapper(HasRangesSwap &value) : value_(value) {}
+
+  HasRangesSwap& operator*() const { return value_; }
+};
+
+static_assert( std::is_invocable_v<IterSwapT, HasRangesSwapWrapper&, HasRangesSwapWrapper&>);
+// Does not satisfy swappable_with, even though swap(X, Y) is valid.
+static_assert(!std::is_invocable_v<IterSwapT, HasRangesSwapWrapper&, int&>);
+static_assert(!std::is_invocable_v<IterSwapT, int&, HasRangesSwapWrapper&>);
+
+struct B;
+
+struct A {
+  bool value = false;
+  A& operator=(const B&) {
+    value = true;
+    return *this;
+  };
+};
+
+struct B {
+  bool value = false;
+  B& operator=(const A&) {
+    value = true;
+    return *this;
+  };
+};
+
+struct MoveOnly2;
+
+struct MoveOnly1 {
+  bool value = false;
+
+  MoveOnly1() = default;
+  MoveOnly1(MoveOnly1&&) = default;
+  MoveOnly1& operator=(MoveOnly1&&) = default;
+  MoveOnly1(const MoveOnly1&) = delete;
+  MoveOnly1& operator=(const MoveOnly1&) = delete;
+
+  MoveOnly1& operator=(MoveOnly2 &&) {
+    value = true;
+    return *this;
+  };
+};
+
+struct MoveOnly2 {
+  bool value = false;
+
+  MoveOnly2() = default;
+  MoveOnly2(MoveOnly2&&) = default;
+  MoveOnly2& operator=(MoveOnly2&&) = default;
+  MoveOnly2(const MoveOnly2&) = delete;
+  MoveOnly2& operator=(const MoveOnly2&) = delete;
+
+  MoveOnly2& operator=(MoveOnly1 &&) {
+    value = true;
+    return *this;
+  };
+};
+
+int main(int, char**) {
+  {
+    int value1 = 0;
+    int value2 = 0;
+    HasIterSwap a(value1), b(value2);
+    std::ranges::iter_swap(a, b);
+    assert(value1 == 1 && value2 == 1);
+  }
+
+  {
+    int value1 = 0;
+    int value2 = 0;
+    HasRangesSwap c(value1), d(value2);
+    HasRangesSwapWrapper cWrapper(c), dWrapper(d);
+    std::ranges::iter_swap(cWrapper, dWrapper);
+    assert(value1 == 1 && value2 == 1);
+  }
+
+  {
+    int value1 = 0;
+    int value2 = 0;
+    HasRangesSwap c(value1), d(value2);
+    std::ranges::iter_swap(HasRangesSwapWrapper(c), HasRangesSwapWrapper(d));
+    assert(value1 == 1 && value2 == 1);
+  }
+
+  {
+    A e; B f;
+    A *ePtr = &e;
+    B *fPtr = &f;
+    std::ranges::iter_swap(ePtr, fPtr);
+    assert(e.value && f.value);
+  }
+
+  {
+    MoveOnly1 g; MoveOnly2 h;
+    std::ranges::iter_swap(&g, &h);
+    assert(g.value && h.value);
+  }
+
+  {
+    auto arr = std::array<move_tracker, 2>();
+    std::ranges::iter_swap(arr.begin(), arr.begin() + 1);
+    assert(arr[0].moves() == 1 && arr[1].moves() == 2);
+  }
+
+  {
+    int buff[2] = {1, 2};
+    std::ranges::iter_swap(buff + 0, buff + 1);
+    assert(buff[0] == 2 && buff[1] == 1);
+
+    std::ranges::iter_swap(cpp20_input_iterator(buff), cpp20_input_iterator(buff + 1));
+    assert(buff[0] == 1 && buff[1] == 2);
+
+    std::ranges::iter_swap(cpp17_input_iterator(buff), cpp17_input_iterator(buff + 1));
+    assert(buff[0] == 2 && buff[1] == 1);
+
+    std::ranges::iter_swap(forward_iterator(buff), forward_iterator(buff + 1));
+    assert(buff[0] == 1 && buff[1] == 2);
+
+    std::ranges::iter_swap(bidirectional_iterator(buff), bidirectional_iterator(buff + 1));
+    assert(buff[0] == 2 && buff[1] == 1);
+
+    std::ranges::iter_swap(random_access_iterator(buff), random_access_iterator(buff + 1));
+    assert(buff[0] == 1 && buff[1] == 2);
+
+    std::ranges::iter_swap(contiguous_iterator(buff), contiguous_iterator(buff + 1));
+    assert(buff[0] == 2 && buff[1] == 1);
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/unqualified_lookup_wrapper.h b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/unqualified_lookup_wrapper.h
index 0f28f21e9b7d1..51f35c3234ec1 100644
--- a/libcxx/test/std/iterators/iterator.requirements/iterator.cust/unqualified_lookup_wrapper.h
+++ b/libcxx/test/std/iterators/iterator.requirements/iterator.cust/unqualified_lookup_wrapper.h
@@ -57,4 +57,23 @@ constexpr int iter_move(some_union& u) noexcept(false) { return u.x; }
 
 } // namespace check_unqualified_lookup
 
+class move_tracker {
+public:
+  move_tracker() = default;
+  constexpr move_tracker(move_tracker&& other) noexcept : moves_{other.moves_ + 1} { other.moves_ = 0; }
+  constexpr move_tracker& operator=(move_tracker&& other) noexcept {
+    moves_ = other.moves_ + 1;
+    other.moves_ = 0;
+    return *this;
+  }
+
+  move_tracker(move_tracker const& other) = delete;
+  move_tracker& operator=(move_tracker const& other) = delete;
+
+  constexpr int moves() const noexcept { return moves_; }
+
+private:
+  int moves_ = 0;
+};
+
 #endif // LIBCPP_TEST_STD_ITERATOR_UNQUALIFIED_LOOKUP_WRAPPER


        


More information about the libcxx-commits mailing list