Skip to content

Commit c63a291

Browse files
[libc][cpp] reverse_iterator support (#85702)
Towards the goal of implementing __cxa_finalize (#85651) I'd like to be able to reverse iterate 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).
1 parent 252e255 commit c63a291

File tree

4 files changed

+162
-0
lines changed

4 files changed

+162
-0
lines changed

libc/src/__support/CPP/array.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_ARRAY_H
1010
#define LLVM_LIBC_SRC___SUPPORT_CPP_ARRAY_H
1111

12+
#include "src/__support/CPP/iterator.h" // reverse_iterator
1213
#include "src/__support/macros/attributes.h"
1314
#include <stddef.h> // For size_t.
1415

@@ -23,6 +24,8 @@ template <class T, size_t N> struct array {
2324
using value_type = T;
2425
using iterator = T *;
2526
using const_iterator = const T *;
27+
using reverse_iterator = cpp::reverse_iterator<iterator>;
28+
using const_reverse_iterator = cpp::reverse_iterator<const_iterator>;
2629

2730
LIBC_INLINE constexpr T *data() { return Data; }
2831
LIBC_INLINE constexpr const T *data() const { return Data; }
@@ -45,9 +48,29 @@ template <class T, size_t N> struct array {
4548

4649
LIBC_INLINE constexpr iterator begin() { return Data; }
4750
LIBC_INLINE constexpr const_iterator begin() const { return Data; }
51+
LIBC_INLINE constexpr const_iterator cbegin() const { return begin(); }
4852

4953
LIBC_INLINE constexpr iterator end() { return Data + N; }
5054
LIBC_INLINE constexpr const_iterator end() const { return Data + N; }
55+
LIBC_INLINE constexpr const_iterator cend() const { return end(); }
56+
57+
LIBC_INLINE constexpr reverse_iterator rbegin() {
58+
return reverse_iterator{end()};
59+
}
60+
LIBC_INLINE constexpr const_reverse_iterator rbegin() const {
61+
return const_reverse_iterator{end()};
62+
}
63+
LIBC_INLINE constexpr const_reverse_iterator crbegin() const {
64+
return rbegin();
65+
}
66+
67+
LIBC_INLINE constexpr reverse_iterator rend() {
68+
return reverse_iterator{begin()};
69+
}
70+
LIBC_INLINE constexpr const_reverse_iterator rend() const {
71+
return const_reverse_iterator{begin()};
72+
}
73+
LIBC_INLINE constexpr const_reverse_iterator crend() const { return rend(); }
5174
};
5275

5376
} // namespace cpp

libc/src/__support/CPP/iterator.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//===-- Standalone implementation of iterator -------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_ITERATOR_H
10+
#define LLVM_LIBC_SRC___SUPPORT_CPP_ITERATOR_H
11+
12+
#include "src/__support/CPP/type_traits/enable_if.h"
13+
#include "src/__support/CPP/type_traits/is_convertible.h"
14+
#include "src/__support/CPP/type_traits/is_same.h"
15+
#include "src/__support/macros/attributes.h"
16+
17+
namespace LIBC_NAMESPACE {
18+
namespace cpp {
19+
20+
template <typename T> struct iterator_traits;
21+
template <typename T> struct iterator_traits<T *> {
22+
using reference = T &;
23+
};
24+
25+
template <typename Iter> class reverse_iterator {
26+
Iter current;
27+
28+
public:
29+
using reference = typename iterator_traits<Iter>::reference;
30+
31+
LIBC_INLINE reverse_iterator() : current() {}
32+
LIBC_INLINE constexpr explicit reverse_iterator(Iter it) : current(it) {}
33+
34+
template <typename Other,
35+
cpp::enable_if_t<!cpp::is_same_v<Iter, Other> &&
36+
cpp::is_convertible_v<const Other &, Iter>,
37+
int> = 0>
38+
LIBC_INLINE constexpr explicit reverse_iterator(const Other &it)
39+
: current(it) {}
40+
41+
LIBC_INLINE constexpr reference operator*() const {
42+
Iter tmp = current;
43+
return *--tmp;
44+
}
45+
LIBC_INLINE constexpr reverse_iterator operator--() {
46+
++current;
47+
return *this;
48+
}
49+
LIBC_INLINE constexpr reverse_iterator &operator++() {
50+
--current;
51+
return *this;
52+
}
53+
LIBC_INLINE constexpr reverse_iterator operator++(int) {
54+
reverse_iterator tmp(*this);
55+
--current;
56+
return tmp;
57+
}
58+
};
59+
60+
} // namespace cpp
61+
} // namespace LIBC_NAMESPACE
62+
63+
#endif // LLVM_LIBC_SRC___SUPPORT_CPP_ITERATOR_H

libc/test/src/__support/CPP/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
add_custom_target(libc-cpp-utils-tests)
22

3+
add_libc_test(
4+
array_test
5+
SUITE
6+
libc-cpp-utils-tests
7+
SRCS
8+
array_test.cpp
9+
DEPENDS
10+
libc.src.__support.CPP.array
11+
)
12+
313
add_libc_test(
414
bit_test
515
SUITE
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//===-- Unittests for Array -----------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/__support/CPP/array.h"
10+
#include "test/UnitTest/Test.h"
11+
12+
using LIBC_NAMESPACE::cpp::array;
13+
14+
TEST(LlvmLibcArrayTest, Basic) {
15+
array<int, 3> a = {0, 1, 2};
16+
17+
ASSERT_EQ(a.data(), &a.front());
18+
ASSERT_EQ(a.front(), 0);
19+
ASSERT_EQ(a.back(), 2);
20+
ASSERT_EQ(a.size(), size_t{3});
21+
ASSERT_EQ(a[1], 1);
22+
ASSERT_FALSE(a.empty());
23+
ASSERT_NE(a.begin(), a.end());
24+
ASSERT_EQ(*a.begin(), a.front());
25+
26+
auto it = a.rbegin();
27+
ASSERT_EQ(*it, 2);
28+
ASSERT_EQ(*(++it), 1);
29+
ASSERT_EQ(*(++it), 0);
30+
31+
for (int &x : a)
32+
ASSERT_GE(x, 0);
33+
}
34+
35+
// Test const_iterator and const variant methods.
36+
TEST(LlvmLibcArrayTest, Const) {
37+
const array<int, 3> z = {3, 4, 5};
38+
39+
ASSERT_EQ(3, z.front());
40+
ASSERT_EQ(4, z[1]);
41+
ASSERT_EQ(5, z.back());
42+
ASSERT_EQ(3, *z.data());
43+
44+
// begin, cbegin, end, cend
45+
array<int, 3>::const_iterator it2 = z.begin();
46+
ASSERT_EQ(*it2, z.front());
47+
it2 = z.cbegin();
48+
ASSERT_EQ(*it2, z.front());
49+
it2 = z.end();
50+
ASSERT_NE(it2, z.begin());
51+
it2 = z.cend();
52+
ASSERT_NE(it2, z.begin());
53+
54+
// rbegin, crbegin, rend, crend
55+
array<int, 3>::const_reverse_iterator it = z.rbegin();
56+
ASSERT_EQ(*it, z.back());
57+
it = z.crbegin();
58+
ASSERT_EQ(*it, z.back());
59+
it = z.rend();
60+
ASSERT_EQ(*--it, z.front());
61+
it = z.crend();
62+
ASSERT_EQ(*--it, z.front());
63+
64+
for (const int &x : z)
65+
ASSERT_GE(x, 0);
66+
}

0 commit comments

Comments
 (0)