[libc-commits] [libc] [libc][cpp] reverse_iterator support (PR #85702)

Nick Desaulniers via libc-commits libc-commits at lists.llvm.org
Tue Mar 19 08:24:33 PDT 2024


https://github.com/nickdesaulniers updated https://github.com/llvm/llvm-project/pull/85702

>From fa0f64addf30b466abbad9d89d78ab68c651bd8d Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Mon, 18 Mar 2024 10:08:24 -0700
Subject: [PATCH 1/2] [libc][cpp] reverse_iterator support

Towards the goal of implementing __cxa_finalize (#85651) I'd like to be able to
reverse iterator over cpp::arrays such as the one used in FixedVector.

Implement the enough iterator support to be able to iterate a cpp::array in
reverse, and add tests.

Of note, reverse iterator's begin() refers to forward iterator's end() (and
vice versa). When dereferenced (operator*), the reverse iterator returns a copy
that's been pre-decremented (the underlying forward iterator is advanced).
---
 libc/src/__support/CPP/array.h             | 23 ++++++++
 libc/src/__support/CPP/iterator.h          | 62 ++++++++++++++++++++
 libc/test/src/__support/CPP/CMakeLists.txt | 10 ++++
 libc/test/src/__support/CPP/array_test.cpp | 66 ++++++++++++++++++++++
 4 files changed, 161 insertions(+)
 create mode 100644 libc/src/__support/CPP/iterator.h
 create mode 100644 libc/test/src/__support/CPP/array_test.cpp

diff --git a/libc/src/__support/CPP/array.h b/libc/src/__support/CPP/array.h
index fb5a79225beb7d..4e69ba003e800b 100644
--- a/libc/src/__support/CPP/array.h
+++ b/libc/src/__support/CPP/array.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_ARRAY_H
 #define LLVM_LIBC_SRC___SUPPORT_CPP_ARRAY_H
 
+#include "src/__support/CPP/iterator.h" // reverse_iterator
 #include "src/__support/macros/attributes.h"
 #include <stddef.h> // For size_t.
 
@@ -23,6 +24,8 @@ template <class T, size_t N> struct array {
   using value_type = T;
   using iterator = T *;
   using const_iterator = const T *;
+  using reverse_iterator = cpp::reverse_iterator<iterator>;
+  using const_reverse_iterator = cpp::reverse_iterator<const_iterator>;
 
   LIBC_INLINE constexpr T *data() { return Data; }
   LIBC_INLINE constexpr const T *data() const { return Data; }
@@ -45,9 +48,29 @@ template <class T, size_t N> struct array {
 
   LIBC_INLINE constexpr iterator begin() { return Data; }
   LIBC_INLINE constexpr const_iterator begin() const { return Data; }
+  LIBC_INLINE constexpr const_iterator cbegin() const { return begin(); }
 
   LIBC_INLINE constexpr iterator end() { return Data + N; }
   LIBC_INLINE constexpr const_iterator end() const { return Data + N; }
+  LIBC_INLINE constexpr const_iterator cend() const { return end(); }
+
+  LIBC_INLINE constexpr reverse_iterator rbegin() {
+    return reverse_iterator{end()};
+  }
+  LIBC_INLINE constexpr const_reverse_iterator rbegin() const {
+    return const_reverse_iterator{end()};
+  }
+  LIBC_INLINE constexpr const_reverse_iterator crbegin() const {
+    return rbegin();
+  }
+
+  LIBC_INLINE constexpr reverse_iterator rend() {
+    return reverse_iterator{begin()};
+  }
+  LIBC_INLINE constexpr const_reverse_iterator rend() const {
+    return const_reverse_iterator{begin()};
+  }
+  LIBC_INLINE constexpr const_reverse_iterator crend() const { return rend(); }
 };
 
 } // namespace cpp
diff --git a/libc/src/__support/CPP/iterator.h b/libc/src/__support/CPP/iterator.h
new file mode 100644
index 00000000000000..1a52f096e80005
--- /dev/null
+++ b/libc/src/__support/CPP/iterator.h
@@ -0,0 +1,62 @@
+//===-- Standalone implementation of iterator -------------------*- 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 LLVM_LIBC_SRC___SUPPORT_CPP_ITERATOR_H
+#define LLVM_LIBC_SRC___SUPPORT_CPP_ITERATOR_H
+
+#include "src/__support/CPP/type_traits/enable_if.h"
+#include "src/__support/CPP/type_traits/is_convertible.h"
+#include "src/__support/CPP/type_traits/is_same.h"
+#include "src/__support/macros/attributes.h"
+
+namespace LIBC_NAMESPACE {
+namespace cpp {
+
+template <typename T> struct iterator_traits;
+template <typename T> struct iterator_traits<T *> {
+  using reference = T &;
+};
+
+template <typename Iter> class reverse_iterator {
+  Iter current;
+
+public:
+  using reference = typename iterator_traits<Iter>::reference;
+
+  reverse_iterator() : current() {}
+  constexpr explicit reverse_iterator(Iter it) : current(it) {}
+
+  template <typename Other,
+            cpp::enable_if_t<!cpp::is_same_v<Iter, Other> &&
+                                 cpp::is_convertible_v<const Other &, Iter>,
+                             int> = 0>
+  constexpr explicit reverse_iterator(const Other &it) : current(it) {}
+
+  constexpr reference operator*() const {
+    Iter tmp = current;
+    return *--tmp;
+  }
+  constexpr reverse_iterator operator--() {
+    ++current;
+    return *this;
+  }
+  constexpr reverse_iterator &operator++() {
+    --current;
+    return *this;
+  }
+  constexpr reverse_iterator operator++(int) {
+    reverse_iterator tmp(*this);
+    --current;
+    return tmp;
+  }
+};
+
+} // namespace cpp
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC___SUPPORT_CPP_ITERATOR_H
diff --git a/libc/test/src/__support/CPP/CMakeLists.txt b/libc/test/src/__support/CPP/CMakeLists.txt
index f94429e03b3cbc..74aa0c705ec467 100644
--- a/libc/test/src/__support/CPP/CMakeLists.txt
+++ b/libc/test/src/__support/CPP/CMakeLists.txt
@@ -1,5 +1,15 @@
 add_custom_target(libc-cpp-utils-tests)
 
+add_libc_test(
+  array_test
+  SUITE
+    libc-cpp-utils-tests
+  SRCS
+    array_test.cpp
+  DEPENDS
+    libc.src.__support.CPP.array
+  )
+
 add_libc_test(
   bit_test
   SUITE
diff --git a/libc/test/src/__support/CPP/array_test.cpp b/libc/test/src/__support/CPP/array_test.cpp
new file mode 100644
index 00000000000000..f2d7bff636e42c
--- /dev/null
+++ b/libc/test/src/__support/CPP/array_test.cpp
@@ -0,0 +1,66 @@
+//===-- Unittests for Array -----------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/CPP/array.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::cpp::array;
+
+TEST(LlvmLibcArrayTest, Basic) {
+  array<int, 3> a = {0, 1, 2};
+
+  ASSERT_EQ(a.data(), &a.front());
+  ASSERT_EQ(a.front(), 0);
+  ASSERT_EQ(a.back(), 2);
+  ASSERT_EQ(a.size(), size_t{3});
+  ASSERT_EQ(a[1], 1);
+  ASSERT_FALSE(a.empty());
+  ASSERT_NE(a.begin(), a.end());
+  ASSERT_EQ(*a.begin(), a.front());
+
+  auto it = a.rbegin();
+  ASSERT_EQ(*it, 2);
+  ASSERT_EQ(*(++it), 1);
+  ASSERT_EQ(*(++it), 0);
+
+  for (int &x : a)
+    ASSERT_GE(x, 0);
+}
+
+// Test const_iterator and const variant methods.
+TEST(LlvmLibcArrayTest, Const) {
+  const array<int, 3> z = {3, 4, 5};
+
+  ASSERT_EQ(3, z.front());
+  ASSERT_EQ(4, z[1]);
+  ASSERT_EQ(5, z.back());
+  ASSERT_EQ(3, *z.data());
+
+  // begin, cbegin, end, cend
+  array<int, 3>::const_iterator it2 = z.begin();
+  ASSERT_EQ(*it2, z.front());
+  it2 = z.cbegin();
+  ASSERT_EQ(*it2, z.front());
+  it2 = z.end();
+  ASSERT_NE(it2, z.begin());
+  it2 = z.cend();
+  ASSERT_NE(it2, z.begin());
+
+  // rbegin, crbegin, rend, crend
+  array<int, 3>::const_reverse_iterator it = z.rbegin();
+  ASSERT_EQ(*it, z.back());
+  it = z.crbegin();
+  ASSERT_EQ(*it, z.back());
+  it = z.rend();
+  ASSERT_EQ(*--it, z.front());
+  it = z.crend();
+  ASSERT_EQ(*--it, z.front());
+
+  for (const int &x : z)
+    ASSERT_GE(x, 0);
+}

>From 965130c138cc3ec1bcd518cd225a5ca3be0059d8 Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Tue, 19 Mar 2024 08:23:55 -0700
Subject: [PATCH 2/2] LIBC_INLINE

---
 libc/src/__support/CPP/iterator.h | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/libc/src/__support/CPP/iterator.h b/libc/src/__support/CPP/iterator.h
index 1a52f096e80005..c5bfb1912c7b74 100644
--- a/libc/src/__support/CPP/iterator.h
+++ b/libc/src/__support/CPP/iterator.h
@@ -28,28 +28,29 @@ template <typename Iter> class reverse_iterator {
 public:
   using reference = typename iterator_traits<Iter>::reference;
 
-  reverse_iterator() : current() {}
-  constexpr explicit reverse_iterator(Iter it) : current(it) {}
+  LIBC_INLINE reverse_iterator() : current() {}
+  LIBC_INLINE constexpr explicit reverse_iterator(Iter it) : current(it) {}
 
   template <typename Other,
             cpp::enable_if_t<!cpp::is_same_v<Iter, Other> &&
                                  cpp::is_convertible_v<const Other &, Iter>,
                              int> = 0>
-  constexpr explicit reverse_iterator(const Other &it) : current(it) {}
+  LIBC_INLINE constexpr explicit reverse_iterator(const Other &it)
+      : current(it) {}
 
-  constexpr reference operator*() const {
+  LIBC_INLINE constexpr reference operator*() const {
     Iter tmp = current;
     return *--tmp;
   }
-  constexpr reverse_iterator operator--() {
+  LIBC_INLINE constexpr reverse_iterator operator--() {
     ++current;
     return *this;
   }
-  constexpr reverse_iterator &operator++() {
+  LIBC_INLINE constexpr reverse_iterator &operator++() {
     --current;
     return *this;
   }
-  constexpr reverse_iterator operator++(int) {
+  LIBC_INLINE constexpr reverse_iterator operator++(int) {
     reverse_iterator tmp(*this);
     --current;
     return tmp;



More information about the libc-commits mailing list