Skip to content

Commit cadae13

Browse files
authored
Implement P2248R8: Enable list-initialization for algorithms (#2398)
Implementing Iterator-based algorithms only. Ranges-based implementation is going on separately. There are no breaking changes. All the breaking changes in C++ standard come into `std::range`s namespace but even they are irrelevant to oneDPL because we implement list-initialization enabling right away, so there is no template parameters swapping. There are two oversights for P2248: - `find_last` was not included. However, it's irrelevant for Iterator-based algorithms because `find_last` exists in `std::ranges` namespace only. - uninitialized_fill was not included. I've implemented that proactively as was requested. There is a NB comment to include this oversight for C++26 but it's not done at this moment. Binary Search-like algorithms from P2248 (e.g., lower_bound, upper_bound, etc.) are also irrelevant because our API for those does not have T-like template parameter.
1 parent d61d6ae commit cadae13

File tree

12 files changed

+659
-13
lines changed

12 files changed

+659
-13
lines changed

include/oneapi/dpl/pstl/glue_algorithm_defs.h

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ template <class _ExecutionPolicy, class _ForwardIterator, class _Predicate>
6464
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator>
6565
find_if_not(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, _Predicate __pred);
6666

67-
template <class _ExecutionPolicy, class _ForwardIterator, class _Tp>
67+
template <class _ExecutionPolicy, class _ForwardIterator,
68+
class _Tp = typename std::iterator_traits<_ForwardIterator>::value_type>
6869
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator>
6970
find(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value);
7071

@@ -104,7 +105,8 @@ adjacent_find(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardItera
104105

105106
// [alg.count]
106107

107-
template <class _ExecutionPolicy, class _ForwardIterator, class _Tp>
108+
template <class _ExecutionPolicy, class _ForwardIterator,
109+
class _Tp = typename std::iterator_traits<_ForwardIterator>::value_type>
108110
oneapi::dpl::__internal::__enable_if_execution_policy<
109111
_ExecutionPolicy, typename ::std::iterator_traits<_ForwardIterator>::difference_type>
110112
count(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value);
@@ -126,12 +128,14 @@ oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _Forward
126128
search(_ExecutionPolicy&& __exec, _ForwardIterator1 __first, _ForwardIterator1 __last, _ForwardIterator2 __s_first,
127129
_ForwardIterator2 __s_last);
128130

129-
template <class _ExecutionPolicy, class _ForwardIterator, class _Size, class _Tp, class _BinaryPredicate>
131+
template <class _ExecutionPolicy, class _ForwardIterator, class _Size,
132+
class _Tp = typename std::iterator_traits<_ForwardIterator>::value_type, class _BinaryPredicate>
130133
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator>
131134
search_n(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, _Size __count,
132135
const _Tp& __value, _BinaryPredicate __pred);
133136

134-
template <class _ExecutionPolicy, class _ForwardIterator, class _Size, class _Tp>
137+
template <class _ExecutionPolicy, class _ForwardIterator, class _Size,
138+
class _Tp = typename std::iterator_traits<_ForwardIterator>::value_type>
135139
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator>
136140
search_n(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, _Size __count,
137141
const _Tp& __value);
@@ -189,17 +193,20 @@ transform_if(_ExecutionPolicy&& __exec, _ForwardIterator1 __first1, _ForwardIter
189193

190194
// [alg.replace]
191195

192-
template <class _ExecutionPolicy, class _ForwardIterator, class _UnaryPredicate, class _Tp>
196+
template <class _ExecutionPolicy, class _ForwardIterator, class _UnaryPredicate,
197+
class _Tp = typename std::iterator_traits<_ForwardIterator>::value_type>
193198
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy>
194199
replace_if(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, _UnaryPredicate __pred,
195200
const _Tp& __new_value);
196201

197-
template <class _ExecutionPolicy, class _ForwardIterator, class _Tp>
202+
template <class _ExecutionPolicy, class _ForwardIterator,
203+
class _Tp = typename std::iterator_traits<_ForwardIterator>::value_type>
198204
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy>
199205
replace(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __old_value,
200206
const _Tp& __new_value);
201207

202-
template <class _ExecutionPolicy, class _ForwardIterator1, class _ForwardIterator2, class _UnaryPredicate, class _Tp>
208+
template <class _ExecutionPolicy, class _ForwardIterator1, class _ForwardIterator2, class _UnaryPredicate,
209+
class _Tp = typename std::iterator_traits<_ForwardIterator2>::value_type>
203210
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator2>
204211
replace_copy_if(_ExecutionPolicy&& __exec, _ForwardIterator1 __first, _ForwardIterator1 __last,
205212
_ForwardIterator2 __result, _UnaryPredicate __pred, const _Tp& __new_value);
@@ -211,11 +218,13 @@ replace_copy(_ExecutionPolicy&& __exec, _ForwardIterator1 __first, _ForwardItera
211218

212219
// [alg.fill]
213220

214-
template <class _ExecutionPolicy, class _ForwardIterator, class _Tp>
221+
template <class _ExecutionPolicy, class _ForwardIterator,
222+
class _Tp = typename std::iterator_traits<_ForwardIterator>::value_type>
215223
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy>
216224
fill(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value);
217225

218-
template <class _ExecutionPolicy, class _ForwardIterator, class _Size, class _Tp>
226+
template <class _ExecutionPolicy, class _ForwardIterator, class _Size,
227+
class _Tp = typename std::iterator_traits<_ForwardIterator>::value_type>
219228
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator>
220229
fill_n(_ExecutionPolicy&& __exec, _ForwardIterator __first, _Size __count, const _Tp& __value);
221230

@@ -235,7 +244,8 @@ oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _Forward
235244
remove_copy_if(_ExecutionPolicy&& __exec, _ForwardIterator1 __first, _ForwardIterator1 __last,
236245
_ForwardIterator2 __result, _Predicate __pred);
237246

238-
template <class _ExecutionPolicy, class _ForwardIterator1, class _ForwardIterator2, class _Tp>
247+
template <class _ExecutionPolicy, class _ForwardIterator1, class _ForwardIterator2,
248+
class _Tp = typename std::iterator_traits<_ForwardIterator1>::value_type>
239249
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator2>
240250
remove_copy(_ExecutionPolicy&& __exec, _ForwardIterator1 __first, _ForwardIterator1 __last, _ForwardIterator2 __result,
241251
const _Tp& __value);
@@ -244,7 +254,8 @@ template <class _ExecutionPolicy, class _ForwardIterator, class _UnaryPredicate>
244254
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator>
245255
remove_if(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, _UnaryPredicate __pred);
246256

247-
template <class _ExecutionPolicy, class _ForwardIterator, class _Tp>
257+
template <class _ExecutionPolicy, class _ForwardIterator,
258+
class _Tp = typename std::iterator_traits<_ForwardIterator>::value_type>
248259
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator>
249260
remove(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value);
250261

include/oneapi/dpl/pstl/glue_memory_defs.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,13 @@ uninitialized_move_n(_ExecutionPolicy&& __exec, _InputIterator __first, _Size __
4545

4646
// [uninitialized.fill]
4747

48-
template <class _ExecutionPolicy, class _ForwardIterator, class _Tp>
48+
template <class _ExecutionPolicy, class _ForwardIterator,
49+
class _Tp = typename std::iterator_traits<_ForwardIterator>::value_type>
4950
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy>
5051
uninitialized_fill(_ExecutionPolicy&& __exec, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value);
5152

52-
template <class _ExecutionPolicy, class _ForwardIterator, class _Size, class _Tp>
53+
template <class _ExecutionPolicy, class _ForwardIterator, class _Size,
54+
class _Tp = typename std::iterator_traits<_ForwardIterator>::value_type>
5355
oneapi::dpl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator>
5456
uninitialized_fill_n(_ExecutionPolicy&& __exec, _ForwardIterator __first, _Size __n, const _Tp& __value);
5557

test/parallel_api/algorithm/alg.modifying.operations/fill.pass.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,87 @@ test_fill_by_type(::std::size_t n)
9797
#endif
9898
}
9999

100+
void test_empty_list_initialization_for_fill()
101+
{
102+
{
103+
std::vector<int> v{3,6,5,4,3,7,8,0,2,4};
104+
oneapi::dpl::fill(oneapi::dpl::execution::seq, v.begin(), v.end(), {});
105+
EXPECT_TRUE(std::count(v.begin(), v.end(), 0) == v.size(), "a sequence is not filled properly by oneapi::dpl::fill with `seq` policy");
106+
}
107+
{
108+
std::vector<int> v{3,6,5,4,3,7,8,0,2,4};
109+
oneapi::dpl::fill(oneapi::dpl::execution::unseq, v.begin(), v.end(), {});
110+
EXPECT_TRUE(std::count(v.begin(), v.end(), 0) == v.size(), "a sequence is not filled properly by oneapi::dpl::fill with `unseq` policy");
111+
}
112+
113+
{
114+
std::vector<TestUtils::DefaultInitializedToOne> v_custom{{3},{6},{5},{4},{3},{7},{8},{2},{1},{4}};
115+
oneapi::dpl::fill(oneapi::dpl::execution::par, v_custom.begin(), v_custom.end(), {});
116+
EXPECT_TRUE(std::count(v_custom.begin(), v_custom.end(), TestUtils::DefaultInitializedToOne{}) == v_custom.size(),
117+
"a sequence is not filled properly by oneapi::dpl::fill with `par` policy");
118+
}
119+
{
120+
std::vector<TestUtils::DefaultInitializedToOne> v_custom{{3},{6},{5},{4},{3},{7},{8},{2},{1},{4}};
121+
oneapi::dpl::fill(oneapi::dpl::execution::par_unseq, v_custom.begin(), v_custom.end(), {});
122+
EXPECT_TRUE(std::count(v_custom.begin(), v_custom.end(), TestUtils::DefaultInitializedToOne{}) == v_custom.size(),
123+
"a sequence is not filled properly by oneapi::dpl::fill with `par_unseq` policy");
124+
}
125+
#if TEST_DPCPP_BACKEND_PRESENT
126+
std::vector<int> v{3,6,5,4,3,7,8,0,2,4};
127+
{
128+
sycl::buffer<int> buf(v);
129+
oneapi::dpl::fill(oneapi::dpl::execution::dpcpp_default, oneapi::dpl::begin(buf), oneapi::dpl::end(buf), {});
130+
}
131+
EXPECT_TRUE(std::count(v.begin(), v.end(), 0) == v.size(), "a sequence is not filled properly by oneapi::dpl::fill with `device_policy` policy");
132+
#endif
133+
}
134+
135+
void test_empty_list_initialization_for_fill_n()
136+
{
137+
constexpr std::size_t fill_number = 6;
138+
{
139+
std::vector<int> v{3,6,5,4,3,7,8,0,2,4};
140+
auto it = oneapi::dpl::fill_n(oneapi::dpl::execution::seq, v.begin(), fill_number, {});
141+
auto count = std::count(v.begin(), v.begin() + fill_number, 0);
142+
EXPECT_TRUE(it == (v.begin() + fill_number), "an incorrect iterator returned from oneapi::dpl::fill_n with `seq` policy");
143+
EXPECT_TRUE(count == fill_number, "a sequence is not filled properly by oneapi::dpl::fill_n with `seq` policy");
144+
}
145+
{
146+
std::vector<int> v{3,6,5,4,3,7,8,0,2,4};
147+
auto it = oneapi::dpl::fill_n(oneapi::dpl::execution::unseq, v.begin(), fill_number, {});
148+
auto count = std::count(v.begin(), v.begin() + fill_number, 0);
149+
EXPECT_TRUE(it == (v.begin() + fill_number), "an incorrect iterator returned from oneapi::dpl::fill_n with `unseq` policy");
150+
EXPECT_TRUE(count == fill_number, "a sequence is not filled properly by oneapi::dpl::fill_n with `unseq` policy");
151+
}
152+
153+
{
154+
std::vector<TestUtils::DefaultInitializedToOne> v_custom{{3},{6},{5},{4},{3},{7},{8},{2},{1},{4}};
155+
auto it = oneapi::dpl::fill_n(oneapi::dpl::execution::par, v_custom.begin(), fill_number, {});
156+
auto count = std::count(v_custom.begin(), v_custom.begin() + fill_number, TestUtils::DefaultInitializedToOne{1});
157+
EXPECT_TRUE(it == (v_custom.begin() + fill_number), "an incorrect iterator returned from oneapi::dpl::fill_n with `par` policy");
158+
EXPECT_TRUE(count == fill_number, "a sequence is not filled properly by oneapi::dpl::fill_n with `par` policy");
159+
}
160+
{
161+
std::vector<TestUtils::DefaultInitializedToOne> v_custom{{3},{6},{5},{4},{3},{7},{8},{2},{1},{4}};
162+
auto it = oneapi::dpl::fill_n(oneapi::dpl::execution::par_unseq, v_custom.begin(), fill_number, {});
163+
auto count = std::count(v_custom.begin(), v_custom.begin() + fill_number, TestUtils::DefaultInitializedToOne{1});
164+
EXPECT_TRUE(it == (v_custom.begin() + fill_number), "an incorrect iterator returned from oneapi::dpl::fill_n with `par_unseq` policy");
165+
EXPECT_TRUE(count == fill_number, "a sequence is not filled properly by oneapi::dpl::fill_n with `par_unseq` policy");
166+
}
167+
#if TEST_DPCPP_BACKEND_PRESENT
168+
std::vector<int> v{3,6,5,4,3,7,8,0,2,4};
169+
std::size_t idx = 0;
170+
{
171+
sycl::buffer<int> buf(v);
172+
auto it = oneapi::dpl::fill_n(oneapi::dpl::execution::dpcpp_default, oneapi::dpl::begin(buf), fill_number, {});
173+
idx = it.get_idx();
174+
EXPECT_TRUE(idx == fill_number, "an incorrect iterator returned from oneapi::dpl::fill_n with `device_policy` policy");
175+
}
176+
auto count = std::count(v.begin(), v.begin() + idx, 0);
177+
EXPECT_TRUE(count == fill_number, "a sequence is not filled properly by oneapi::dpl::fill_n with `device_policy` policy");
178+
#endif
179+
}
180+
100181
int
101182
main()
102183
{
@@ -108,5 +189,8 @@ main()
108189
test_fill_by_type<float64_t>(n);
109190
}
110191

192+
test_empty_list_initialization_for_fill();
193+
test_empty_list_initialization_for_fill_n();
194+
111195
return done();
112196
}

test/parallel_api/algorithm/alg.modifying.operations/remove.pass.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,56 @@ struct test_non_const
105105
}
106106
};
107107

108+
void test_empty_list_initialization()
109+
{
110+
{
111+
std::vector<int> v{3,6,0,4,0,7,8,0,3,4};
112+
std::vector<int> expected{3,6,4,7,8,3,4};
113+
auto it = oneapi::dpl::remove(oneapi::dpl::execution::seq, v.begin(), v.end(), {});
114+
EXPECT_TRUE(it == v.begin() + 7, "not all empty list-initialized values are properly removed by oneapi::dpl::remove with `seq` policy");
115+
v.erase(it, v.end());
116+
EXPECT_TRUE(v == expected, "wrong effect from calling oneapi::dpl::remove with empty list-initialized value and with `seq` policy");
117+
}
118+
{
119+
std::vector<int> v{3,6,0,4,0,7,8,0,3,4};
120+
std::vector<int> expected{3,6,4,7,8,3,4};
121+
auto it = oneapi::dpl::remove(oneapi::dpl::execution::unseq, v.begin(), v.end(), {});
122+
EXPECT_TRUE(it == v.begin() + 7, "not all empty list-initialized values are properly removed by oneapi::dpl::remove with `unseq` policy");
123+
v.erase(it, v.end());
124+
EXPECT_TRUE(v == expected, "wrong effect from calling oneapi::dpl::remove with empty list-initialized value and with `unseq` policy");
125+
}
126+
127+
{
128+
std::vector<TestUtils::DefaultInitializedToOne> v_custom{{3},{1},{5},{1},{3},{1},{8},{2},{0},{1}};
129+
std::vector<TestUtils::DefaultInitializedToOne> expected_custom{{3},{5},{3},{8},{2},{0}};
130+
auto it = oneapi::dpl::remove(oneapi::dpl::execution::par, v_custom.begin(), v_custom.end(), {});
131+
EXPECT_TRUE(it == v_custom.begin() + 6, "not all empty list-initialized values are properly removed by oneapi::dpl::remove with `par` policy");
132+
v_custom.erase(it, v_custom.end());
133+
EXPECT_TRUE(v_custom == expected_custom, "wrong effect from calling oneapi::dpl::remove with empty list-initialized value and with `par` policy");
134+
}
135+
{
136+
std::vector<TestUtils::DefaultInitializedToOne> v_custom{{3},{1},{5},{1},{3},{1},{8},{2},{0},{1}};
137+
std::vector<TestUtils::DefaultInitializedToOne> expected_custom{{3},{5},{3},{8},{2},{0}};
138+
auto it = oneapi::dpl::remove(oneapi::dpl::execution::par_unseq, v_custom.begin(), v_custom.end(), {});
139+
EXPECT_TRUE(it == v_custom.begin() + 6, "not all empty list-initialized values are properly removed by oneapi::dpl::remove with `par_unseq` policy");
140+
v_custom.erase(it, v_custom.end());
141+
EXPECT_TRUE(v_custom == expected_custom, "wrong effect from calling oneapi::dpl::remove with empty list-initialized value and with `par_unseq` policy");
142+
}
143+
#if TEST_DPCPP_BACKEND_PRESENT
144+
std::vector<int> v{3,6,0,4,0,7,8,0,3,4};
145+
std::vector<int> expected{3,6,4,7,8,3,4};
146+
std::size_t idx = 0;
147+
{
148+
sycl::buffer<int> buf(v);
149+
auto it = oneapi::dpl::remove(oneapi::dpl::execution::dpcpp_default, oneapi::dpl::begin(buf), oneapi::dpl::end(buf), {});
150+
idx = it.get_idx();
151+
EXPECT_TRUE(idx == 7, "not all empty list-initialized values are properly removed by oneapi::dpl::remove with `device_policy` policy");
152+
}
153+
v.erase(v.begin() + idx, v.end());
154+
EXPECT_TRUE(v == expected, "wrong effect from calling oneapi::dpl::remove with empty list-initialized value and with `device_policy` policy");
155+
#endif
156+
}
157+
108158
int
109159
main()
110160
{
@@ -133,5 +183,7 @@ main()
133183
EXPECT_TRUE(MemoryChecker::alive_objects() == 0, "wrong effect from remove,remove_if: number of ctor and dtor calls is not equal");
134184
#endif
135185

186+
test_empty_list_initialization();
187+
136188
return done();
137189
}

0 commit comments

Comments
 (0)