Skip to content

Commit 3fbd3ea

Browse files
committed
[libc++] Implement [P0769] "Add shift to algorithm" (shift_left, shift_right)
I believe this is a complete implementation of std::shift_left and std::shift_right from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0769r2.pdf Some test cases copied-with-modification from D60027. Differential Revision: https://reviews.llvm.org/D93819
1 parent 3395a33 commit 3fbd3ea

File tree

9 files changed

+393
-48
lines changed

9 files changed

+393
-48
lines changed

libcxx/docs/Cxx2aStatusPaperStatus.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"`P0722R3 <https://wg21.link/P0722R3>`__","CWG","Efficient sized delete for variable sized classes","Rapperswil","|Complete|","9.0"
3939
"`P0758R1 <https://wg21.link/P0758R1>`__","LWG","Implicit conversion traits and utility functions","Rapperswil","|Complete|",""
4040
"`P0759R1 <https://wg21.link/P0759R1>`__","LWG","fpos Requirements","Rapperswil","|Complete|","11.0"
41-
"`P0769R2 <https://wg21.link/P0769R2>`__","LWG","Add shift to <algorithm>","Rapperswil","",""
41+
"`P0769R2 <https://wg21.link/P0769R2>`__","LWG","Add shift to <algorithm>","Rapperswil","|Complete|","12.0"
4242
"`P0788R3 <https://wg21.link/P0788R3>`__","LWG","Standard Library Specification in a Concepts and Contracts World","Rapperswil","*Removed in Cologne*","n/a"
4343
"`P0879R0 <https://wg21.link/P0879R0>`__","LWG","Constexpr for swap and swap related functions Also resolves LWG issue 2800.","Rapperswil","",""
4444
"`P0887R1 <https://wg21.link/P0887R1>`__","LWG","The identity metafunction","Rapperswil","|Complete|","8.0"

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ Status
266266
------------------------------------------------- -----------------
267267
``__cpp_lib_semaphore`` ``201907L``
268268
------------------------------------------------- -----------------
269-
``__cpp_lib_shift`` *unimplemented*
269+
``__cpp_lib_shift`` ``201806L``
270270
------------------------------------------------- -----------------
271271
``__cpp_lib_smart_ptr_for_overwrite`` *unimplemented*
272272
------------------------------------------------- -----------------

libcxx/include/algorithm

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,16 @@ template<class RandomAccessIterator, class UniformRandomNumberGenerator>
301301
void shuffle(RandomAccessIterator first, RandomAccessIterator last,
302302
UniformRandomNumberGenerator&& g);
303303
304+
template<class ForwardIterator>
305+
constexpr ForwardIterator
306+
shift_left(ForwardIterator first, ForwardIterator last,
307+
typename iterator_traits<ForwardIterator>::difference_type n); // C++20
308+
309+
template<class ForwardIterator>
310+
constexpr ForwardIterator
311+
shift_right(ForwardIterator first, ForwardIterator last,
312+
typename iterator_traits<ForwardIterator>::difference_type n); // C++20
313+
304314
template <class InputIterator, class Predicate>
305315
constexpr bool // constexpr in C++20
306316
is_partitioned(InputIterator first, InputIterator last, Predicate pred);
@@ -3259,6 +3269,111 @@ template<class _RandomAccessIterator, class _UniformRandomNumberGenerator>
32593269
}
32603270
}
32613271

3272+
#if _LIBCPP_STD_VER > 17
3273+
3274+
// shift_left, shift_right
3275+
3276+
template <class _ForwardIterator>
3277+
inline _LIBCPP_INLINE_VISIBILITY constexpr
3278+
_ForwardIterator
3279+
shift_left(_ForwardIterator __first, _ForwardIterator __last,
3280+
typename iterator_traits<_ForwardIterator>::difference_type __n)
3281+
{
3282+
if (__n == 0) {
3283+
return __last;
3284+
}
3285+
3286+
_ForwardIterator __m = __first;
3287+
if constexpr (__is_cpp17_random_access_iterator<_ForwardIterator>::value) {
3288+
if (__n >= __last - __first) {
3289+
return __first;
3290+
}
3291+
__m += __n;
3292+
} else {
3293+
for (; __n > 0; --__n) {
3294+
if (__m == __last) {
3295+
return __first;
3296+
}
3297+
++__m;
3298+
}
3299+
}
3300+
return _VSTD::move(__m, __last, __first);
3301+
}
3302+
3303+
template <class _ForwardIterator>
3304+
inline _LIBCPP_INLINE_VISIBILITY constexpr
3305+
_ForwardIterator
3306+
shift_right(_ForwardIterator __first, _ForwardIterator __last,
3307+
typename iterator_traits<_ForwardIterator>::difference_type __n)
3308+
{
3309+
if (__n == 0) {
3310+
return __first;
3311+
}
3312+
3313+
if constexpr (__is_cpp17_random_access_iterator<_ForwardIterator>::value) {
3314+
decltype(__n) __d = __last - __first;
3315+
if (__n >= __d) {
3316+
return __last;
3317+
}
3318+
_ForwardIterator __m = __first + (__d - __n);
3319+
return _VSTD::move_backward(__first, __m, __last);
3320+
} else if constexpr (__is_cpp17_bidirectional_iterator<_ForwardIterator>::value) {
3321+
_ForwardIterator __m = __last;
3322+
for (; __n > 0; --__n) {
3323+
if (__m == __first) {
3324+
return __last;
3325+
}
3326+
--__m;
3327+
}
3328+
return _VSTD::move_backward(__first, __m, __last);
3329+
} else {
3330+
_ForwardIterator __ret = __first;
3331+
for (; __n > 0; --__n) {
3332+
if (__ret == __last) {
3333+
return __last;
3334+
}
3335+
++__ret;
3336+
}
3337+
3338+
// We have an __n-element scratch space from __first to __ret.
3339+
// Slide an __n-element window [__trail, __lead) from left to right.
3340+
// We're essentially doing swap_ranges(__first, __ret, __trail, __lead)
3341+
// over and over; but once __lead reaches __last we needn't bother
3342+
// to save the values of elements [__trail, __last).
3343+
3344+
auto __trail = __first;
3345+
auto __lead = __ret;
3346+
while (__trail != __ret) {
3347+
if (__lead == __last) {
3348+
_VSTD::move(__first, __trail, __ret);
3349+
return __ret;
3350+
}
3351+
++__trail;
3352+
++__lead;
3353+
}
3354+
3355+
_ForwardIterator __mid = __first;
3356+
while (true) {
3357+
if (__lead == __last) {
3358+
__trail = _VSTD::move(__mid, __ret, __trail);
3359+
_VSTD::move(__first, __mid, __trail);
3360+
return __ret;
3361+
}
3362+
swap(*__mid, *__trail);
3363+
++__mid;
3364+
++__trail;
3365+
++__lead;
3366+
if (__mid == __ret) {
3367+
__mid = __first;
3368+
}
3369+
}
3370+
}
3371+
}
3372+
3373+
#endif // _LIBCPP_STD_VER > 17
3374+
3375+
// is_partitioned
3376+
32623377
template <class _InputIterator, class _Predicate>
32633378
_LIBCPP_NODISCARD_EXT _LIBCPP_CONSTEXPR_AFTER_CXX17 bool
32643379
is_partitioned(_InputIterator __first, _InputIterator __last, _Predicate __pred)

libcxx/include/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ __cpp_lib_void_t 201411L <type_traits>
339339
# if !defined(_LIBCPP_HAS_NO_THREADS)
340340
# define __cpp_lib_semaphore 201907L
341341
# endif
342-
// # define __cpp_lib_shift 201806L
342+
# define __cpp_lib_shift 201806L
343343
// # define __cpp_lib_smart_ptr_for_overwrite 202002L
344344
// # define __cpp_lib_source_location 201907L
345345
# define __cpp_lib_span 202002L
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//===----------------------------------------------------------------------===//
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+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
11+
// <algorithm>
12+
13+
// template<class ForwardIterator>
14+
// constexpr ForwardIterator
15+
// shift_left(ForwardIterator first, ForwardIterator last,
16+
// typename iterator_traits<ForwardIterator>::difference_type n);
17+
18+
#include <algorithm>
19+
#include <cassert>
20+
21+
#include "test_macros.h"
22+
#include "test_iterators.h"
23+
#include "MoveOnly.h"
24+
25+
template<class T, class Iter>
26+
constexpr bool test()
27+
{
28+
int orig[] = {3,1,4,1,5, 9,2,6,5,3, 5,8,9,7,9};
29+
T work[] = {3,1,4,1,5, 9,2,6,5,3, 5,8,9,7,9};
30+
31+
for (int n = 0; n <= 15; ++n) {
32+
for (int k = 0; k <= n+2; ++k) {
33+
std::copy(orig, orig+n, work);
34+
Iter it = std::shift_left(Iter(work), Iter(work+n), k);
35+
if (0 <= k && k < n) {
36+
assert(it == Iter(work+n-k));
37+
assert(std::equal(orig+k, orig+n, work, work+n-k));
38+
} else {
39+
assert(it == Iter(work));
40+
assert(std::equal(orig, orig+n, work, work+n));
41+
}
42+
}
43+
}
44+
45+
// n == 0
46+
{
47+
T input[] = { 0, 1, 2 };
48+
const T expected[] = { 0, 1, 2 };
49+
Iter b = Iter(std::begin(input));
50+
Iter e = Iter(std::end(input));
51+
Iter it = std::shift_left(b, e, 0);
52+
assert(std::equal(std::begin(expected), std::end(expected), b, e));
53+
assert(it == e);
54+
}
55+
56+
// n > 0 && n < len
57+
{
58+
T input[] = { 0, 1, 2 };
59+
const T expected[] = { 1, 2 };
60+
Iter b = Iter(std::begin(input));
61+
Iter e = Iter(std::end(input));
62+
Iter it = std::shift_left(b, e, 1);
63+
assert(std::equal(std::begin(expected), std::end(expected), b, it));
64+
}
65+
{
66+
T input[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
67+
const T expected[] = { 3, 4, 5, 6, 7, 8 };
68+
Iter b = Iter(std::begin(input));
69+
Iter e = Iter(std::end(input));
70+
Iter it = std::shift_left(b, e, 2);
71+
assert(std::equal(std::begin(expected), std::end(expected), b, it));
72+
}
73+
{
74+
T input[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
75+
const T expected[] = { 7, 8 };
76+
Iter b = Iter(std::begin(input));
77+
Iter e = Iter(std::end(input));
78+
Iter it = std::shift_left(b, e, 6);
79+
assert(std::equal(std::begin(expected), std::end(expected), b, it));
80+
}
81+
82+
// n == len
83+
{
84+
T input[] = { 0, 1, 2 };
85+
const T expected[] = { 0, 1, 2 };
86+
Iter b = Iter(std::begin(input));
87+
Iter e = Iter(std::end(input));
88+
Iter it = std::shift_left(b, e, std::size(input));
89+
assert(std::equal(std::begin(expected), std::end(expected), b, e));
90+
assert(it == b);
91+
}
92+
93+
// n > len
94+
{
95+
T input[] = { 0, 1, 2 };
96+
const T expected[] = { 0, 1, 2 };
97+
Iter b = Iter(std::begin(input));
98+
Iter e = Iter(std::end(input));
99+
Iter it = std::shift_left(b, e, std::size(input) + 1);
100+
assert(std::equal(std::begin(expected), std::end(expected), b, e));
101+
assert(it == b);
102+
}
103+
104+
return true;
105+
}
106+
107+
int main(int, char**)
108+
{
109+
test<int, forward_iterator<int*>>();
110+
test<int, bidirectional_iterator<int*>>();
111+
test<int, random_access_iterator<int*>>();
112+
test<int, int*>();
113+
test<MoveOnly, forward_iterator<MoveOnly*>>();
114+
test<MoveOnly, bidirectional_iterator<MoveOnly*>>();
115+
test<MoveOnly, random_access_iterator<MoveOnly*>>();
116+
test<MoveOnly, MoveOnly*>();
117+
118+
static_assert(test<int, forward_iterator<int*>>());
119+
static_assert(test<int, bidirectional_iterator<int*>>());
120+
static_assert(test<int, random_access_iterator<int*>>());
121+
static_assert(test<int, int*>());
122+
static_assert(test<MoveOnly, forward_iterator<MoveOnly*>>());
123+
static_assert(test<MoveOnly, bidirectional_iterator<MoveOnly*>>());
124+
static_assert(test<MoveOnly, random_access_iterator<MoveOnly*>>());
125+
static_assert(test<MoveOnly, MoveOnly*>());
126+
127+
return 0;
128+
}

0 commit comments

Comments
 (0)