Skip to content

Commit 854a4f2

Browse files
[libc++] Implement part of P2562R1: constexpr std::inplace_merge (#129008)
Drive-by: - Adds `constexpr_random.h` for pseudo-randomizing or shuffling in tests for constant evaluation.
1 parent 7a9473b commit 854a4f2

File tree

7 files changed

+681
-212
lines changed

7 files changed

+681
-212
lines changed

libcxx/include/__algorithm/inplace_merge.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <__functional/identity.h>
2323
#include <__iterator/iterator_traits.h>
2424
#include <__iterator/reverse_iterator.h>
25+
#include <__memory/construct_at.h>
2526
#include <__memory/destruct_n.h>
2627
#include <__memory/unique_ptr.h>
2728
#include <__memory/unique_temporary_buffer.h>
@@ -106,13 +107,13 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __buffered_inplace_merg
106107
value_type* __p = __buff;
107108
for (_BidirectionalIterator __i = __first; __i != __middle;
108109
__d.template __incr<value_type>(), (void)++__i, (void)++__p)
109-
::new ((void*)__p) value_type(_IterOps<_AlgPolicy>::__iter_move(__i));
110+
std::__construct_at(__p, _IterOps<_AlgPolicy>::__iter_move(__i));
110111
std::__half_inplace_merge<_AlgPolicy>(__buff, __p, __middle, __last, __first, __comp);
111112
} else {
112113
value_type* __p = __buff;
113114
for (_BidirectionalIterator __i = __middle; __i != __last;
114115
__d.template __incr<value_type>(), (void)++__i, (void)++__p)
115-
::new ((void*)__p) value_type(_IterOps<_AlgPolicy>::__iter_move(__i));
116+
std::__construct_at(__p, _IterOps<_AlgPolicy>::__iter_move(__i));
116117
typedef reverse_iterator<_BidirectionalIterator> _RBi;
117118
typedef reverse_iterator<value_type*> _Rv;
118119
typedef __invert<_Compare> _Inverted;
@@ -203,7 +204,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void __inplace_merge(
203204
}
204205

205206
template <class _AlgPolicy, class _BidirectionalIterator, class _Compare>
206-
_LIBCPP_HIDE_FROM_ABI void __inplace_merge(
207+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __inplace_merge(
207208
_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last, _Compare&& __comp) {
208209
typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
209210
typedef typename iterator_traits<_BidirectionalIterator>::difference_type difference_type;
@@ -223,14 +224,14 @@ _LIBCPP_HIDE_FROM_ABI void __inplace_merge(
223224
}
224225

225226
template <class _BidirectionalIterator, class _Compare>
226-
inline _LIBCPP_HIDE_FROM_ABI void inplace_merge(
227+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void inplace_merge(
227228
_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last, _Compare __comp) {
228229
std::__inplace_merge<_ClassicAlgPolicy>(
229230
std::move(__first), std::move(__middle), std::move(__last), static_cast<__comp_ref_type<_Compare> >(__comp));
230231
}
231232

232233
template <class _BidirectionalIterator>
233-
inline _LIBCPP_HIDE_FROM_ABI void
234+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
234235
inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last) {
235236
std::inplace_merge(std::move(__first), std::move(__middle), std::move(__last), __less<>());
236237
}

libcxx/include/algorithm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,11 +1609,11 @@ template <class InputIterator1, class InputIterator2, class OutputIterator, clas
16091609
InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);
16101610
16111611
template <class BidirectionalIterator>
1612-
void
1612+
constexpr void // constexpr since C++26
16131613
inplace_merge(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last);
16141614
16151615
template <class BidirectionalIterator, class Compare>
1616-
void
1616+
constexpr void // constexpr since C++26
16171617
inplace_merge(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last, Compare comp);
16181618
16191619
template <class InputIterator1, class InputIterator2>

libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge.pass.cpp

Lines changed: 100 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -8,116 +8,130 @@
88

99
// <algorithm>
1010

11-
// template<BidirectionalIterator Iter>
12-
// requires ShuffleIterator<Iter>
13-
// && LessThanComparable<Iter::value_type>
14-
// void
15-
// inplace_merge(Iter first, Iter middle, Iter last);
11+
// template<class BidirectionalIterator>
12+
// constexpr void // constexpr since C++26
13+
// inplace_merge(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last);
1614

1715
#include <algorithm>
1816
#include <cassert>
19-
#include <random>
2017
#include <vector>
2118

2219
#include "count_new.h"
20+
#include "constexpr_random.h"
2321
#include "test_iterators.h"
2422
#include "test_macros.h"
2523

2624
#if TEST_STD_VER >= 11
2725
struct S {
28-
S() : i_(0) {}
29-
S(int i) : i_(i) {}
30-
31-
S(const S& rhs) : i_(rhs.i_) {}
32-
S( S&& rhs) : i_(rhs.i_) { rhs.i_ = -1; }
33-
34-
S& operator =(const S& rhs) { i_ = rhs.i_; return *this; }
35-
S& operator =( S&& rhs) { i_ = rhs.i_; rhs.i_ = -2; assert(this != &rhs); return *this; }
36-
S& operator =(int i) { i_ = i; return *this; }
37-
38-
bool operator <(const S& rhs) const { return i_ < rhs.i_; }
39-
bool operator ==(const S& rhs) const { return i_ == rhs.i_; }
40-
bool operator ==(int i) const { return i_ == i; }
41-
42-
void set(int i) { i_ = i; }
43-
44-
int i_;
45-
};
26+
TEST_CONSTEXPR_CXX26 S() : i_(0) {}
27+
TEST_CONSTEXPR_CXX26 S(int i) : i_(i) {}
28+
29+
TEST_CONSTEXPR_CXX26 S(const S& rhs) : i_(rhs.i_) {}
30+
TEST_CONSTEXPR_CXX26 S(S&& rhs) : i_(rhs.i_) { rhs.i_ = -1; }
31+
32+
TEST_CONSTEXPR_CXX26 S& operator=(const S& rhs) {
33+
i_ = rhs.i_;
34+
return *this;
35+
}
36+
TEST_CONSTEXPR_CXX26 S& operator=(S&& rhs) {
37+
i_ = rhs.i_;
38+
rhs.i_ = -2;
39+
assert(this != &rhs);
40+
return *this;
41+
}
42+
TEST_CONSTEXPR_CXX26 S& operator=(int i) {
43+
i_ = i;
44+
return *this;
45+
}
46+
47+
TEST_CONSTEXPR_CXX26 bool operator<(const S& rhs) const { return i_ < rhs.i_; }
48+
TEST_CONSTEXPR_CXX26 bool operator==(const S& rhs) const { return i_ == rhs.i_; }
49+
TEST_CONSTEXPR_CXX26 bool operator==(int i) const { return i_ == i; }
50+
51+
TEST_CONSTEXPR_CXX26 void set(int i) { i_ = i; }
52+
53+
int i_;
54+
};
4655
#endif
4756

48-
std::mt19937 randomness;
49-
50-
template <class Iter>
51-
void
52-
test_one(unsigned N, unsigned M)
53-
{
54-
typedef typename std::iterator_traits<Iter>::value_type value_type;
55-
assert(M <= N);
56-
value_type* ia = new value_type[N];
57-
for (unsigned i = 0; i < N; ++i)
58-
ia[i] = i;
59-
std::shuffle(ia, ia+N, randomness);
60-
std::sort(ia, ia+M);
61-
std::sort(ia+M, ia+N);
62-
std::inplace_merge(Iter(ia), Iter(ia+M), Iter(ia+N));
63-
if(N > 0)
64-
{
65-
assert(ia[0] == 0);
66-
assert(ia[N-1] == static_cast<value_type>(N-1));
67-
assert(std::is_sorted(ia, ia+N));
68-
}
69-
delete [] ia;
57+
template <class Iter, class RandSrc>
58+
TEST_CONSTEXPR_CXX26 void test_one(unsigned N, unsigned M, RandSrc& randomness) {
59+
assert(M <= N);
60+
61+
typedef typename std::iterator_traits<Iter>::value_type value_type;
62+
value_type* ia = new value_type[N];
63+
for (unsigned i = 0; i < N; ++i)
64+
ia[i] = i;
65+
support::shuffle(ia, ia + N, randomness);
66+
std::sort(ia, ia + M);
67+
std::sort(ia + M, ia + N);
68+
std::inplace_merge(Iter(ia), Iter(ia + M), Iter(ia + N));
69+
if (N > 0) {
70+
assert(ia[0] == 0);
71+
assert(ia[N - 1] == static_cast<value_type>(N - 1));
72+
assert(std::is_sorted(ia, ia + N));
73+
}
74+
delete[] ia;
7075
}
7176

72-
template <class Iter>
73-
void
74-
test(unsigned N)
75-
{
76-
test_one<Iter>(N, 0);
77-
test_one<Iter>(N, N/4);
78-
test_one<Iter>(N, N/2);
79-
test_one<Iter>(N, 3*N/4);
80-
test_one<Iter>(N, N);
77+
template <class Iter, class RandSrc>
78+
TEST_CONSTEXPR_CXX26 void test(unsigned N, RandSrc& randomness) {
79+
test_one<Iter>(N, 0, randomness);
80+
test_one<Iter>(N, N / 4, randomness);
81+
test_one<Iter>(N, N / 2, randomness);
82+
test_one<Iter>(N, 3 * N / 4, randomness);
83+
test_one<Iter>(N, N, randomness);
8184
}
8285

83-
template <class Iter>
84-
void
85-
test()
86-
{
87-
test_one<Iter>(0, 0);
88-
test_one<Iter>(1, 0);
89-
test_one<Iter>(1, 1);
90-
test_one<Iter>(2, 0);
91-
test_one<Iter>(2, 1);
92-
test_one<Iter>(2, 2);
93-
test_one<Iter>(3, 0);
94-
test_one<Iter>(3, 1);
95-
test_one<Iter>(3, 2);
96-
test_one<Iter>(3, 3);
97-
test<Iter>(4);
98-
test<Iter>(100);
99-
test<Iter>(1000);
86+
template <class Iter, class RandSrc>
87+
TEST_CONSTEXPR_CXX26 void test(RandSrc& randomness) {
88+
test_one<Iter>(0, 0, randomness);
89+
test_one<Iter>(1, 0, randomness);
90+
test_one<Iter>(1, 1, randomness);
91+
test_one<Iter>(2, 0, randomness);
92+
test_one<Iter>(2, 1, randomness);
93+
test_one<Iter>(2, 2, randomness);
94+
test_one<Iter>(3, 0, randomness);
95+
test_one<Iter>(3, 1, randomness);
96+
test_one<Iter>(3, 2, randomness);
97+
test_one<Iter>(3, 3, randomness);
98+
test<Iter>(4, randomness);
99+
test<Iter>(50, randomness);
100+
if (!TEST_IS_CONSTANT_EVALUATED) { // avoid exceeding the constant evaluation step limit
101+
test<Iter>(100, randomness);
102+
test<Iter>(1000, randomness);
103+
}
100104
}
101105

102-
int main(int, char**)
103-
{
104-
test<bidirectional_iterator<int*> >();
105-
test<random_access_iterator<int*> >();
106-
test<int*>();
106+
TEST_CONSTEXPR_CXX26 bool test() {
107+
support::simple_random_generator randomness;
108+
109+
test<bidirectional_iterator<int*> >(randomness);
110+
test<random_access_iterator<int*> >(randomness);
111+
test<int*>(randomness);
107112

108113
#if TEST_STD_VER >= 11
109-
test<bidirectional_iterator<S*> >();
110-
test<random_access_iterator<S*> >();
111-
test<S*>();
114+
test<bidirectional_iterator<S*> >(randomness);
115+
test<random_access_iterator<S*> >(randomness);
116+
test<S*>(randomness);
112117
#endif
113118

119+
return true;
120+
}
121+
122+
int main(int, char**) {
123+
test();
124+
#if TEST_STD_VER >= 26
125+
static_assert(test());
126+
#endif // TEST_STD_VER >= 26
127+
114128
#if TEST_STD_VER >= 11 && !defined(TEST_HAS_NO_EXCEPTIONS)
115-
{
116-
std::vector<int> vec(150, 3);
117-
getGlobalMemCounter()->throw_after = 0;
118-
std::inplace_merge(vec.begin(), vec.begin() + 100, vec.end());
119-
assert(std::all_of(vec.begin(), vec.end(), [](int i) { return i == 3; }));
120-
}
129+
if (!TEST_IS_CONSTANT_EVALUATED) {
130+
std::vector<int> vec(150, 3);
131+
getGlobalMemCounter()->throw_after = 0;
132+
std::inplace_merge(vec.begin(), vec.begin() + 100, vec.end());
133+
assert(std::all_of(vec.begin(), vec.end(), [](int i) { return i == 3; }));
134+
}
121135
#endif // TEST_STD_VER >= 11 && !defined(TEST_HAS_NO_EXCEPTIONS)
122136

123137
return 0;

0 commit comments

Comments
 (0)