• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

STEllAR-GROUP / hpx / #882

31 Aug 2023 07:44PM UTC coverage: 41.798% (-44.7%) from 86.546%
#882

push

19442 of 46514 relevant lines covered (41.8%)

126375.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

38.3
/libs/core/algorithms/include/hpx/parallel/algorithms/for_loop.hpp
1
//  Copyright (c) 2007-2025 Hartmut Kaiser
2
//  Copyright (c) 2016 Thomas Heller
3
//
4
//  SPDX-License-Identifier: BSL-1.0
5
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
6
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7

8
/// \file parallel/algorithms/for_loop.hpp
9
/// \page hpx::experimental::for_loop, hpx::experimental::for_loop_strided, hpx::experimental::for_loop_n, hpx::experimental::for_loop_n_strided
10
/// \headerfile hpx/algorithm.hpp
11

12
#pragma once
13

14
#if defined(DOXYGEN)
15
namespace hpx { namespace experimental {
16

17
    /// The for_loop implements loop functionality over a range specified by
18
    /// integral or iterator bounds. For the iterator case, these algorithms
19
    /// resemble for_each from the Parallelism TS, but leave to the programmer
20
    /// when and if to dereference the iterator.
21
    ///
22
    /// The execution of for_loop without specifying an execution policy is
23
    /// equivalent to specifying \a hpx::execution::seq as the execution
24
    /// policy.
25
    ///
26
    /// \tparam I           The type of the iteration variable. This could be
27
    ///                     an (forward) iterator type or an integral type.
28
    /// \tparam Args        A parameter pack, it's last element is a function
29
    ///                     object to be invoked for each iteration, the others
30
    ///                     have to be either conforming to the induction or
31
    ///                     reduction concept.
32
    ///
33
    /// \param first        Refers to the beginning of the sequence of elements
34
    ///                     the algorithm will be applied to.
35
    /// \param last         Refers to the end of the sequence of elements
36
    ///                     the algorithm will be applied to.
37
    /// \param args         The last element of this parameter pack is the
38
    ///                     function (object) to invoke, while the remaining
39
    ///                     elements of the parameter pack are instances of
40
    ///                     either induction or reduction objects.
41
    ///                     The function (or function object) which will be
42
    ///                     invoked for each of the elements in the sequence
43
    ///                     specified by [first, last) should expose a signature
44
    ///                     equivalent to:
45
    ///                     \code
46
    ///                     <ignored> pred(I const& a, ...);
47
    ///                     \endcode \n
48
    ///                     The signature does not need to have const&. It will
49
    ///                     receive the current value of the iteration variable
50
    ///                     and one argument for each of the induction or
51
    ///                     reduction objects passed to the algorithms,
52
    ///                     representing their current values.
53
    ///
54
    /// Requires: \a I shall be an integral type or meet the requirements
55
    ///           of an input iterator type. The \a args parameter pack shall
56
    ///           have at least one element, comprising objects returned by
57
    ///           invocations of \a reduction and/or \a induction function
58
    ///           templates followed by exactly one element invocable
59
    ///           element-access function, \a f. \a f shall meet the
60
    ///           requirements of \a MoveConstructible.
61
    ///
62
    /// Effects:  Applies \a f to each element in the input sequence, with
63
    ///           additional arguments corresponding to the reductions and
64
    ///           inductions in the \a args parameter pack. The length of the
65
    ///           input sequence is \a last - \a first.
66
    ///
67
    /// The first element in the input sequence is specified by \a first. Each
68
    /// subsequent element is generated by incrementing the previous element.
69
    ///
70
    /// \note As described in the C++ standard, arithmetic on non-random-access
71
    ///       iterators is performed using advance and distance.
72
    ///
73
    /// \note The order of the elements of the input sequence is important for
74
    ///       determining ordinal position of an application of \a f, even
75
    ///       though the applications themselves may be unordered.
76
    ///
77
    /// Along with an element from the input sequence, for each member of the
78
    /// \a args parameter pack excluding \a f, an additional argument is passed
79
    /// to each application of \a f as follows:
80
    ///
81
    /// If the pack member is an object returned by a call to a reduction
82
    /// function listed in section, then the
83
    /// additional argument is a reference to a view of that reduction object.
84
    /// If the pack member is an object returned by a call to induction, then
85
    /// the additional argument is the induction value for that induction object
86
    /// corresponding to the position of the application of \a f in the input
87
    /// sequence.
88
    ///
89
    /// Complexity: Applies \a f exactly once for each element of the input
90
    ///             sequence.
91
    ///
92
    /// Remarks: If \a f returns a result, the result is ignored.
93
    ///
94
    template <typename I, typename... Args>
95
    void for_loop(std::decay_t<I> first, I last, Args&&... args);
96

97
    /// The for_loop implements loop functionality over a range specified by
98
    /// integral or iterator bounds. For the iterator case, these algorithms
99
    /// resemble for_each from the Parallelism TS, but leave to the programmer
100
    /// when and if to dereference the iterator. Executed according to the
101
    /// policy.
102
    ///
103
    /// \tparam ExPolicy    The type of the execution policy to use (deduced).
104
    ///                     It describes the manner in which the execution
105
    ///                     of the algorithm may be parallelized and the manner
106
    ///                     in which it applies user-provided function objects.
107
    /// \tparam I           The type of the iteration variable. This could be
108
    ///                     an (forward) iterator type or an integral type.
109
    /// \tparam Args        A parameter pack, it's last element is a function
110
    ///                     object to be invoked for each iteration, the others
111
    ///                     have to be either conforming to the induction or
112
    ///                     reduction concept.
113
    ///
114
    /// \param policy       The execution policy to use for the scheduling of
115
    ///                     the iterations.
116
    /// \param first        Refers to the beginning of the sequence of elements
117
    ///                     the algorithm will be applied to.
118
    /// \param last         Refers to the end of the sequence of elements
119
    ///                     the algorithm will be applied to.
120
    /// \param args         The last element of this parameter pack is the
121
    ///                     function (object) to invoke, while the remaining
122
    ///                     elements of the parameter pack are instances of
123
    ///                     either induction or reduction objects.
124
    ///                     The function (or function object) which will be
125
    ///                     invoked for each of the elements in the sequence
126
    ///                     specified by [first, last) should expose a signature
127
    ///                     equivalent to:
128
    ///                     \code
129
    ///                     <ignored> pred(I const& a, ...);
130
    ///                     \endcode \n
131
    ///                     The signature does not need to have const&. It will
132
    ///                     receive the current value of the iteration variable
133
    ///                     and one argument for each of the induction or
134
    ///                     reduction objects passed to the algorithms,
135
    ///                     representing their current values.
136
    ///
137
    /// Requires: \a I shall be an integral type or meet the requirements
138
    ///           of an input iterator type. The \a args parameter pack shall
139
    ///           have at least one element, comprising objects returned by
140
    ///           invocations of \a reduction and/or \a induction function
141
    ///           templates followed by exactly one element invocable
142
    ///           element-access function, \a f. \a f shall meet the
143
    ///           requirements of \a MoveConstructible.
144
    ///
145
    /// Effects:  Applies \a f to each element in the input sequence, with
146
    ///           additional arguments corresponding to the reductions and
147
    ///           inductions in the \a args parameter pack. The length of the
148
    ///           input sequence is \a last - \a first.
149
    ///
150
    /// The first element in the input sequence is specified by \a first. Each
151
    /// subsequent element is generated by incrementing the previous element.
152
    ///
153
    /// \note As described in the C++ standard, arithmetic on non-random-access
154
    ///       iterators is performed using advance and distance.
155
    ///
156
    /// \note The order of the elements of the input sequence is important for
157
    ///       determining ordinal position of an application of \a f, even
158
    ///       though the applications themselves may be unordered.
159
    ///
160
    /// Along with an element from the input sequence, for each member of the
161
    /// \a args parameter pack excluding \a f, an additional argument is passed
162
    /// to each application of \a f as follows:
163
    ///
164
    /// If the pack member is an object returned by a call to a reduction
165
    /// function listed in section, then the
166
    /// additional argument is a reference to a view of that reduction object.
167
    /// If the pack member is an object returned by a call to induction, then
168
    /// the additional argument is the induction value for that induction object
169
    /// corresponding to the position of the application of \a f in the input
170
    /// sequence.
171
    ///
172
    /// Complexity: Applies \a f exactly once for each element of the input
173
    ///             sequence.
174
    ///
175
    /// Remarks: If \a f returns a result, the result is ignored.
176
    ///
177
    /// \returns  The \a for_loop algorithm returns a
178
    ///           \a hpx::future<void> if the execution policy is of
179
    ///           type
180
    ///           \a hpx::execution::sequenced_task_policy or
181
    ///           \a hpx::execution::parallel_task_policy and returns \a void
182
    ///           otherwise.
183
    ///
184
    template <typename ExPolicy, typename I, typename... Args>
185
    <unspecified> for_loop(
186
        ExPolicy&& policy, std::decay_t<I> first, I last, Args&&... args);
187

188
    /// The for_loop_strided implements loop functionality over a range
189
    /// specified by integral or iterator bounds. For the iterator case, these
190
    /// algorithms resemble for_each from the Parallelism TS, but leave to the
191
    /// programmer when and if to dereference the iterator.
192
    ///
193
    /// The execution of for_loop without specifying an execution policy is
194
    /// equivalent to specifying \a hpx::execution::seq as the execution
195
    /// policy.
196
    ///
197
    /// \tparam I           The type of the iteration variable. This could be
198
    ///                     an (forward) iterator type or an integral type.
199
    /// \tparam S           The type of the stride variable. This should be
200
    ///                     an integral type.
201
    /// \tparam Args        A parameter pack, it's last element is a function
202
    ///                     object to be invoked for each iteration, the others
203
    ///                     have to be either conforming to the induction or
204
    ///                     reduction concept.
205
    ///
206
    /// \param first        Refers to the beginning of the sequence of elements
207
    ///                     the algorithm will be applied to.
208
    /// \param last         Refers to the end of the sequence of elements
209
    ///                     the algorithm will be applied to.
210
    /// \param stride       Refers to the stride of the iteration steps. This
211
    ///                     shall have non-zero value and shall be negative
212
    ///                     only if I has integral type or meets the requirements
213
    ///                     of a bidirectional iterator.
214
    /// \param args         The last element of this parameter pack is the
215
    ///                     function (object) to invoke, while the remaining
216
    ///                     elements of the parameter pack are instances of
217
    ///                     either induction or reduction objects.
218
    ///                     The function (or function object) which will be
219
    ///                     invoked for each of the elements in the sequence
220
    ///                     specified by [first, last) should expose a signature
221
    ///                     equivalent to:
222
    ///                     \code
223
    ///                     <ignored> pred(I const& a, ...);
224
    ///                     \endcode \n
225
    ///                     The signature does not need to have const&. It will
226
    ///                     receive the current value of the iteration variable
227
    ///                     and one argument for each of the induction or
228
    ///                     reduction objects passed to the algorithms,
229
    ///                     representing their current values.
230
    ///
231
    /// Requires: \a I shall be an integral type or meet the requirements
232
    ///           of an input iterator type. The \a args parameter pack shall
233
    ///           have at least one element, comprising objects returned by
234
    ///           invocations of \a reduction and/or \a induction function
235
    ///           templates followed by exactly one element invocable
236
    ///           element-access function, \a f. \a f shall meet the
237
    ///           requirements of \a MoveConstructible.
238
    ///
239
    /// Effects:  Applies \a f to each element in the input sequence, with
240
    ///           additional arguments corresponding to the reductions and
241
    ///           inductions in the \a args parameter pack. The length of the
242
    ///           input sequence is \a last - \a first.
243
    ///
244
    /// The first element in the input sequence is specified by \a first. Each
245
    /// subsequent element is generated by incrementing the previous element.
246
    ///
247
    /// \note As described in the C++ standard, arithmetic on non-random-access
248
    ///       iterators is performed using advance and distance.
249
    ///
250
    /// \note The order of the elements of the input sequence is important for
251
    ///       determining ordinal position of an application of \a f, even
252
    ///       though the applications themselves may be unordered.
253
    ///
254
    /// Along with an element from the input sequence, for each member of the
255
    /// \a args parameter pack excluding \a f, an additional argument is passed
256
    /// to each application of \a f as follows:
257
    ///
258
    /// If the pack member is an object returned by a call to a reduction
259
    /// function listed in section, then the
260
    /// additional argument is a reference to a view of that reduction object.
261
    /// If the pack member is an object returned by a call to induction, then
262
    /// the additional argument is the induction value for that induction object
263
    /// corresponding to the position of the application of \a f in the input
264
    /// sequence.
265
    ///
266
    /// Complexity: Applies \a f exactly once for each element of the input
267
    ///             sequence.
268
    ///
269
    /// Remarks: If \a f returns a result, the result is ignored.
270
    ///
271
    template <typename I, typename S, typename... Args>
272
    void for_loop_strided(
273
        std::decay_t<I> first, I last, S stride, Args&&... args);
274

275
    /// The for_loop_strided implements loop functionality over a range
276
    /// specified by integral or iterator bounds. For the iterator case, these
277
    /// algorithms resemble for_each from the Parallelism TS, but leave to the
278
    /// programmer when and if to dereference the iterator. Executed according
279
    /// to the policy.
280
    ///
281
    /// \tparam ExPolicy    The type of the execution policy to use (deduced).
282
    ///                     It describes the manner in which the execution
283
    ///                     of the algorithm may be parallelized and the manner
284
    ///                     in which it applies user-provided function objects.
285
    /// \tparam I           The type of the iteration variable. This could be
286
    ///                     an (forward) iterator type or an integral type.
287
    /// \tparam S           The type of the stride variable. This should be
288
    ///                     an integral type.
289
    /// \tparam Args        A parameter pack, it's last element is a function
290
    ///                     object to be invoked for each iteration, the others
291
    ///                     have to be either conforming to the induction or
292
    ///                     reduction concept.
293
    ///
294
    /// \param policy       The execution policy to use for the scheduling of
295
    ///                     the iterations.
296
    /// \param first        Refers to the beginning of the sequence of elements
297
    ///                     the algorithm will be applied to.
298
    /// \param last         Refers to the end of the sequence of elements
299
    ///                     the algorithm will be applied to.
300
    /// \param stride       Refers to the stride of the iteration steps. This
301
    ///                     shall have non-zero value and shall be negative
302
    ///                     only if I has integral type or meets the requirements
303
    ///                     of a bidirectional iterator.
304
    /// \param args         The last element of this parameter pack is the
305
    ///                     function (object) to invoke, while the remaining
306
    ///                     elements of the parameter pack are instances of
307
    ///                     either induction or reduction objects.
308
    ///                     The function (or function object) which will be
309
    ///                     invoked for each of the elements in the sequence
310
    ///                     specified by [first, last) should expose a signature
311
    ///                     equivalent to:
312
    ///                     \code
313
    ///                     <ignored> pred(I const& a, ...);
314
    ///                     \endcode \n
315
    ///                     The signature does not need to have const&. It will
316
    ///                     receive the current value of the iteration variable
317
    ///                     and one argument for each of the induction or
318
    ///                     reduction objects passed to the algorithms,
319
    ///                     representing their current values.
320
    ///
321
    /// Requires: \a I shall be an integral type or meet the requirements
322
    ///           of an input iterator type. The \a args parameter pack shall
323
    ///           have at least one element, comprising objects returned by
324
    ///           invocations of \a reduction and/or \a induction function
325
    ///           templates followed by exactly one element invocable
326
    ///           element-access function, \a f. \a f shall meet the
327
    ///           requirements of \a MoveConstructible.
328
    ///
329
    /// Effects:  Applies \a f to each element in the input sequence, with
330
    ///           additional arguments corresponding to the reductions and
331
    ///           inductions in the \a args parameter pack. The length of the
332
    ///           input sequence is \a last - \a first.
333
    ///
334
    /// The first element in the input sequence is specified by \a first. Each
335
    /// subsequent element is generated by incrementing the previous element.
336
    ///
337
    /// \note As described in the C++ standard, arithmetic on non-random-access
338
    ///       iterators is performed using advance and distance.
339
    ///
340
    /// \note The order of the elements of the input sequence is important for
341
    ///       determining ordinal position of an application of \a f, even
342
    ///       though the applications themselves may be unordered.
343
    ///
344
    /// Along with an element from the input sequence, for each member of the
345
    /// \a args parameter pack excluding \a f, an additional argument is passed
346
    /// to each application of \a f as follows:
347
    ///
348
    /// If the pack member is an object returned by a call to a reduction
349
    /// function listed in section, then the
350
    /// additional argument is a reference to a view of that reduction object.
351
    /// If the pack member is an object returned by a call to induction, then
352
    /// the additional argument is the induction value for that induction object
353
    /// corresponding to the position of the application of \a f in the input
354
    /// sequence.
355
    ///
356
    /// Complexity: Applies \a f exactly once for each element of the input
357
    ///             sequence.
358
    ///
359
    /// Remarks: If \a f returns a result, the result is ignored.
360
    ///
361
    /// \returns  The \a for_loop_strided algorithm returns a
362
    ///           \a hpx::future<void> if the execution policy is of
363
    ///           type
364
    ///           \a hpx::execution::sequenced_task_policy or
365
    ///           \a hpx::execution::parallel_task_policy and returns \a void
366
    ///           otherwise.
367
    ///
368
    template <typename ExPolicy, typename I, typename S, typename... Args>
369
    <unspecified> for_loop_strided(ExPolicy&& policy, std::decay_t<I> first,
370
        I last, S stride, Args&&... args);
371

372
    /// The for_loop_n implements loop functionality over a range specified by
373
    /// integral or iterator bounds. For the iterator case, these algorithms
374
    /// resemble for_each from the Parallelism TS, but leave to the programmer
375
    /// when and if to dereference the iterator.
376
    ///
377
    /// The execution of for_loop_n without specifying an execution policy is
378
    /// equivalent to specifying \a hpx::execution::seq as the execution
379
    /// policy.
380
    ///
381
    /// \tparam I           The type of the iteration variable. This could be
382
    ///                     an (forward) iterator type or an integral type.
383
    /// \tparam Size        The type of a non-negative integral value specifying
384
    ///                     the number of items to iterate over.
385
    /// \tparam Args        A parameter pack, it's last element is a function
386
    ///                     object to be invoked for each iteration, the others
387
    ///                     have to be either conforming to the induction or
388
    ///                     reduction concept.
389
    ///
390
    /// \param first        Refers to the beginning of the sequence of elements
391
    ///                     the algorithm will be applied to.
392
    /// \param size         Refers to the number of items the algorithm will be
393
    ///                     applied to.
394
    /// \param args         The last element of this parameter pack is the
395
    ///                     function (object) to invoke, while the remaining
396
    ///                     elements of the parameter pack are instances of
397
    ///                     either induction or reduction objects.
398
    ///                     The function (or function object) which will be
399
    ///                     invoked for each of the elements in the sequence
400
    ///                     specified by [first, last) should expose a signature
401
    ///                     equivalent to:
402
    ///                     \code
403
    ///                     <ignored> pred(I const& a, ...);
404
    ///                     \endcode \n
405
    ///                     The signature does not need to have const&. It will
406
    ///                     receive the current value of the iteration variable
407
    ///                     and one argument for each of the induction or
408
    ///                     reduction objects passed to the algorithms,
409
    ///                     representing their current values.
410
    ///
411
    /// Requires: \a I shall be an integral type or meet the requirements
412
    ///           of an input iterator type. The \a args parameter pack shall
413
    ///           have at least one element, comprising objects returned by
414
    ///           invocations of \a reduction and/or \a induction function
415
    ///           templates followed by exactly one element invocable
416
    ///           element-access function, \a f. \a f shall meet the
417
    ///           requirements of \a MoveConstructible.
418
    ///
419
    /// Effects:  Applies \a f to each element in the input sequence, with
420
    ///           additional arguments corresponding to the reductions and
421
    ///           inductions in the \a args parameter pack. The length of the
422
    ///           input sequence is \a last - \a first.
423
    ///
424
    /// The first element in the input sequence is specified by \a first. Each
425
    /// subsequent element is generated by incrementing the previous element.
426
    ///
427
    /// \note As described in the C++ standard, arithmetic on non-random-access
428
    ///       iterators is performed using advance and distance.
429
    ///
430
    /// \note The order of the elements of the input sequence is important for
431
    ///       determining ordinal position of an application of \a f, even
432
    ///       though the applications themselves may be unordered.
433
    ///
434
    /// Along with an element from the input sequence, for each member of the
435
    /// \a args parameter pack excluding \a f, an additional argument is passed
436
    /// to each application of \a f as follows:
437
    ///
438
    /// If the pack member is an object returned by a call to a reduction
439
    /// function listed in section, then the
440
    /// additional argument is a reference to a view of that reduction object.
441
    /// If the pack member is an object returned by a call to induction, then
442
    /// the additional argument is the induction value for that induction object
443
    /// corresponding to the position of the application of \a f in the input
444
    /// sequence.
445
    ///
446
    /// Complexity: Applies \a f exactly once for each element of the input
447
    ///             sequence.
448
    ///
449
    /// Remarks: If \a f returns a result, the result is ignored.
450
    ///
451
    template <typename I, typename Size, typename... Args>
452
    void for_loop_n(I first, Size size, Args&&... args);
453

454
    /// The for_loop_n implements loop functionality over a range specified by
455
    /// integral or iterator bounds. For the iterator case, these algorithms
456
    /// resemble for_each from the Parallelism TS, but leave to the programmer
457
    /// when and if to dereference the iterator. Executed according to the
458
    /// policy.
459
    ///
460
    /// \tparam ExPolicy    The type of the execution policy to use (deduced).
461
    ///                     It describes the manner in which the execution
462
    ///                     of the algorithm may be parallelized and the manner
463
    ///                     in which it applies user-provided function objects.
464
    /// \tparam I           The type of the iteration variable. This could be
465
    ///                     an (forward) iterator type or an integral type.
466
    /// \tparam Size        The type of a non-negative integral value specifying
467
    ///                     the number of items to iterate over.
468
    /// \tparam Args        A parameter pack, it's last element is a function
469
    ///                     object to be invoked for each iteration, the others
470
    ///                     have to be either conforming to the induction or
471
    ///                     reduction concept.
472
    ///
473
    /// \param policy       The execution policy to use for the scheduling of
474
    ///                     the iterations.
475
    /// \param first        Refers to the beginning of the sequence of elements
476
    ///                     the algorithm will be applied to.
477
    /// \param size         Refers to the number of items the algorithm will be
478
    ///                     applied to.
479
    /// \param args         The last element of this parameter pack is the
480
    ///                     function (object) to invoke, while the remaining
481
    ///                     elements of the parameter pack are instances of
482
    ///                     either induction or reduction objects.
483
    ///                     The function (or function object) which will be
484
    ///                     invoked for each of the elements in the sequence
485
    ///                     specified by [first, last) should expose a signature
486
    ///                     equivalent to:
487
    ///                     \code
488
    ///                     <ignored> pred(I const& a, ...);
489
    ///                     \endcode \n
490
    ///                     The signature does not need to have const&. It will
491
    ///                     receive the current value of the iteration variable
492
    ///                     and one argument for each of the induction or
493
    ///                     reduction objects passed to the algorithms,
494
    ///                     representing their current values.
495
    ///
496
    /// Requires: \a I shall be an integral type or meet the requirements
497
    ///           of an input iterator type. The \a args parameter pack shall
498
    ///           have at least one element, comprising objects returned by
499
    ///           invocations of \a reduction and/or \a induction function
500
    ///           templates followed by exactly one element invocable
501
    ///           element-access function, \a f. \a f shall meet the
502
    ///           requirements of \a  MoveConstructible.
503
    ///
504
    /// Effects:  Applies \a f to each element in the input sequence, with
505
    ///           additional arguments corresponding to the reductions and
506
    ///           inductions in the \a args parameter pack. The length of the
507
    ///           input sequence is \a last - \a first.
508
    ///
509
    /// The first element in the input sequence is specified by \a first. Each
510
    /// subsequent element is generated by incrementing the previous element.
511
    ///
512
    /// \note As described in the C++ standard, arithmetic on non-random-access
513
    ///       iterators is performed using advance and distance.
514
    ///
515
    /// \note The order of the elements of the input sequence is important for
516
    ///       determining ordinal position of an application of \a f, even
517
    ///       though the applications themselves may be unordered.
518
    ///
519
    /// Along with an element from the input sequence, for each member of the
520
    /// \a args parameter pack excluding \a f, an additional argument is passed
521
    /// to each application of \a f as follows:
522
    ///
523
    /// If the pack member is an object returned by a call to a reduction
524
    /// function listed in section, then the
525
    /// additional argument is a reference to a view of that reduction object.
526
    /// If the pack member is an object returned by a call to induction, then
527
    /// the additional argument is the induction value for that induction object
528
    /// corresponding to the position of the application of \a f in the input
529
    /// sequence.
530
    ///
531
    /// Complexity: Applies \a f exactly once for each element of the input
532
    ///             sequence.
533
    ///
534
    /// Remarks: If \a f returns a result, the result is ignored.
535
    ///
536
    /// \returns  The \a for_loop_n algorithm returns a
537
    ///           \a hpx::future<void> if the execution policy is of
538
    ///           type
539
    ///           \a hpx::execution::sequenced_task_policy or
540
    ///           \a hpx::execution::parallel_task_policy and returns \a void
541
    ///           otherwise.
542
    ///
543
    template <typename ExPolicy, typename I, typename Size, typename... Args>
544
    <unspecified> for_loop_n(
545
        ExPolicy&& policy, I first, Size size, Args&&... args);
546

547
    /// The for_loop_n_strided implements loop functionality over a range
548
    /// specified by integral or iterator bounds. For the iterator case, these
549
    /// algorithms resemble for_each from the Parallelism TS, but leave to the
550
    /// programmer when and if to dereference the iterator.
551
    ///
552
    /// The execution of for_loop without specifying an execution policy is
553
    /// equivalent to specifying \a hpx::execution::seq as the execution
554
    /// policy.
555
    ///
556
    /// \tparam I           The type of the iteration variable. This could be
557
    ///                     an (forward) iterator type or an integral type.
558
    /// \tparam Size        The type of a non-negative integral value specifying
559
    ///                     the number of items to iterate over.
560
    /// \tparam S           The type of the stride variable. This should be
561
    ///                     an integral type.
562
    /// \tparam Args        A parameter pack, it's last element is a function
563
    ///                     object to be invoked for each iteration, the others
564
    ///                     have to be either conforming to the induction or
565
    ///                     reduction concept.
566
    ///
567
    /// \param first        Refers to the beginning of the sequence of elements
568
    ///                     the algorithm will be applied to.
569
    /// \param size         Refers to the number of items the algorithm will be
570
    ///                     applied to.
571
    /// \param stride       Refers to the stride of the iteration steps. This
572
    ///                     shall have non-zero value and shall be negative
573
    ///                     only if I has integral type or meets the requirements
574
    ///                     of a bidirectional iterator.
575
    /// \param args         The last element of this parameter pack is the
576
    ///                     function (object) to invoke, while the remaining
577
    ///                     elements of the parameter pack are instances of
578
    ///                     either induction or reduction objects.
579
    ///                     The function (or function object) which will be
580
    ///                     invoked for each of the elements in the sequence
581
    ///                     specified by [first, last) should expose a signature
582
    ///                     equivalent to:
583
    ///                     \code
584
    ///                     <ignored> pred(I const& a, ...);
585
    ///                     \endcode \n
586
    ///                     The signature does not need to have const&. It will
587
    ///                     receive the current value of the iteration variable
588
    ///                     and one argument for each of the induction or
589
    ///                     reduction objects passed to the algorithms,
590
    ///                     representing their current values.
591
    ///
592
    /// Requires: \a I shall be an integral type or meet the requirements
593
    ///           of an input iterator type. The \a args parameter pack shall
594
    ///           have at least one element, comprising objects returned by
595
    ///           invocations of \a reduction and/or \a induction function
596
    ///           templates followed by exactly one element invocable
597
    ///           element-access function, \a f. \a f shall meet the
598
    ///           requirements of \a MoveConstructible.
599
    ///
600
    /// Effects:  Applies \a f to each element in the input sequence, with
601
    ///           additional arguments corresponding to the reductions and
602
    ///           inductions in the \a args parameter pack. The length of the
603
    ///           input sequence is \a last - \a first.
604
    ///
605
    /// The first element in the input sequence is specified by \a first. Each
606
    /// subsequent element is generated by incrementing the previous element.
607
    ///
608
    /// \note As described in the C++ standard, arithmetic on non-random-access
609
    ///       iterators is performed using advance and distance.
610
    ///
611
    /// \note The order of the elements of the input sequence is important for
612
    ///       determining ordinal position of an application of \a f, even
613
    ///       though the applications themselves may be unordered.
614
    ///
615
    /// Along with an element from the input sequence, for each member of the
616
    /// \a args parameter pack excluding \a f, an additional argument is passed
617
    /// to each application of \a f as follows:
618
    ///
619
    /// If the pack member is an object returned by a call to a reduction
620
    /// function listed in section, then the
621
    /// additional argument is a reference to a view of that reduction object.
622
    /// If the pack member is an object returned by a call to induction, then
623
    /// the additional argument is the induction value for that induction object
624
    /// corresponding to the position of the application of \a f in the input
625
    /// sequence.
626
    ///
627
    /// Complexity: Applies \a f exactly once for each element of the input
628
    ///             sequence.
629
    ///
630
    /// Remarks: If \a f returns a result, the result is ignored.
631
    ///
632
    template <typename I, typename Size, typename S, typename... Args>
633
    void for_loop_n_strided(I first, Size size, S stride, Args&&... args);
634

635
    /// The for_loop_n_strided implements loop functionality over a range
636
    /// specified by integral or iterator bounds. For the iterator case, these
637
    /// algorithms resemble for_each from the Parallelism TS, but leave to the
638
    /// programmer when and if to dereference the iterator. Executed according
639
    /// to the policy.
640
    ///
641
    /// \tparam ExPolicy    The type of the execution policy to use (deduced).
642
    ///                     It describes the manner in which the execution
643
    ///                     of the algorithm may be parallelized and the manner
644
    ///                     in which it applies user-provided function objects.
645
    /// \tparam I           The type of the iteration variable. This could be
646
    ///                     an (forward) iterator type or an integral type.
647
    /// \tparam Size        The type of a non-negative integral value specifying
648
    ///                     the number of items to iterate over.
649
    /// \tparam S           The type of the stride variable. This should be
650
    ///                     an integral type.
651
    /// \tparam Args        A parameter pack, it's last element is a function
652
    ///                     object to be invoked for each iteration, the others
653
    ///                     have to be either conforming to the induction or
654
    ///                     reduction concept.
655
    ///
656
    /// \param policy       The execution policy to use for the scheduling of
657
    ///                     the iterations.
658
    /// \param first        Refers to the beginning of the sequence of elements
659
    ///                     the algorithm will be applied to.
660
    /// \param size         Refers to the number of items the algorithm will be
661
    ///                     applied to.
662
    /// \param stride       Refers to the stride of the iteration steps. This
663
    ///                     shall have non-zero value and shall be negative
664
    ///                     only if I has integral type or meets the requirements
665
    ///                     of a bidirectional iterator.
666
    /// \param args         The last element of this parameter pack is the
667
    ///                     function (object) to invoke, while the remaining
668
    ///                     elements of the parameter pack are instances of
669
    ///                     either induction or reduction objects.
670
    ///                     The function (or function object) which will be
671
    ///                     invoked for each of the elements in the sequence
672
    ///                     specified by [first, last) should expose a signature
673
    ///                     equivalent to:
674
    ///                     \code
675
    ///                     <ignored> pred(I const& a, ...);
676
    ///                     \endcode \n
677
    ///                     The signature does not need to have const&. It will
678
    ///                     receive the current value of the iteration variable
679
    ///                     and one argument for each of the induction or
680
    ///                     reduction objects passed to the algorithms,
681
    ///                     representing their current values.
682
    ///
683
    /// Requires: \a I shall be an integral type or meet the requirements
684
    ///           of an input iterator type. The \a args parameter pack shall
685
    ///           have at least one element, comprising objects returned by
686
    ///           invocations of \a reduction and/or \a induction function
687
    ///           templates followed by exactly one element invocable
688
    ///           element-access function, \a f. \a f shall meet the
689
    ///           requirements of \a MoveConstructible.
690
    ///
691
    /// Effects:  Applies \a f to each element in the input sequence, with
692
    ///           additional arguments corresponding to the reductions and
693
    ///           inductions in the \a args parameter pack. The length of the
694
    ///           input sequence is \a last - \a first.
695
    ///
696
    /// The first element in the input sequence is specified by \a first. Each
697
    /// subsequent element is generated by incrementing the previous element.
698
    ///
699
    /// \note As described in the C++ standard, arithmetic on non-random-access
700
    ///       iterators is performed using advance and distance.
701
    ///
702
    /// \note The order of the elements of the input sequence is important for
703
    ///       determining ordinal position of an application of \a f, even
704
    ///       though the applications themselves may be unordered.
705
    ///
706
    /// Along with an element from the input sequence, for each member of the
707
    /// \a args parameter pack excluding \a f, an additional argument is passed
708
    /// to each application of \a f as follows:
709
    ///
710
    /// If the pack member is an object returned by a call to a reduction
711
    /// function listed in section, then the
712
    /// additional argument is a reference to a view of that reduction object.
713
    /// If the pack member is an object returned by a call to induction, then
714
    /// the additional argument is the induction value for that induction object
715
    /// corresponding to the position of the application of \a f in the input
716
    /// sequence.
717
    ///
718
    /// Complexity: Applies \a f exactly once for each element of the input
719
    ///             sequence.
720
    ///
721
    /// Remarks: If \a f returns a result, the result is ignored.
722
    ///
723
    /// \returns  The \a for_loop_n_strided algorithm returns a
724
    ///           \a hpx::future<void> if the execution policy is of
725
    ///           type
726
    ///           \a hpx::execution::sequenced_task_policy or
727
    ///           \a hpx::execution::parallel_task_policy and returns \a void
728
    ///           otherwise.
729
    ///
730
    template <typename ExPolicy, typename I, typename Size, typename S,
731
        typename... Args>
732
    <unspecified> for_loop_n_strided(
733
        ExPolicy&& policy, I first, Size size, S stride, Args&&... args);
734
}}    // namespace hpx::experimental
735

736
#else
737

738
#include <hpx/config.hpp>
739
#include <hpx/assert.hpp>
740
#include <hpx/modules/concepts.hpp>
741
#include <hpx/modules/datastructures.hpp>
742
#include <hpx/modules/execution.hpp>
743
#include <hpx/modules/executors.hpp>
744
#include <hpx/modules/iterator_support.hpp>
745
#include <hpx/modules/tag_invoke.hpp>
746
#include <hpx/modules/threading_base.hpp>
747
#include <hpx/modules/type_support.hpp>
748
#include <hpx/parallel/algorithms/detail/dispatch.hpp>
749
#include <hpx/parallel/algorithms/for_loop_induction.hpp>
750
#include <hpx/parallel/algorithms/for_loop_reduction.hpp>
751
#include <hpx/parallel/util/adapt_sharing_mode.hpp>
752
#include <hpx/parallel/util/detail/algorithm_result.hpp>
753
#include <hpx/parallel/util/detail/sender_util.hpp>
754
#include <hpx/parallel/util/loop.hpp>
755
#include <hpx/parallel/util/partitioner.hpp>
756

757
#include <cstddef>
758
#include <cstdint>
759
#include <type_traits>
760
#include <utility>
761

762
namespace hpx::parallel {
763

764
    // for_loop
765
    namespace detail {
766

767
        /// \cond NOINTERNAL
768

769
        ///////////////////////////////////////////////////////////////////////
770
        HPX_HAS_XXX_TRAIT_DEF(needs_current_thread_num);
771

772
        ///////////////////////////////////////////////////////////////////////
773
        HPX_CXX_EXPORT template <typename... Ts, std::size_t... Is>
774
        HPX_HOST_DEVICE constexpr void init_iteration(hpx::tuple<Ts...>& args,
775
            hpx::util::index_pack<Is...>, std::size_t part_index,
776
            std::size_t current_thread) noexcept
777
        {
778
            (hpx::get<Is>(args).init_iteration(part_index, current_thread),
779
                ...);
780
        }
781

782
        HPX_CXX_EXPORT template <typename... Ts, std::size_t... Is, typename F,
783
            typename B>
784
        HPX_HOST_DEVICE HPX_FORCEINLINE constexpr void invoke_iteration(
785
            hpx::tuple<Ts...>& args, hpx::util::index_pack<Is...>, F&& f,
786
            B part_begin, std::size_t current_thread)
787
        {
788
            HPX_INVOKE(HPX_FORWARD(F, f), part_begin,
789
                hpx::get<Is>(args).iteration_value(current_thread)...);
790
        }
791

792
        HPX_CXX_EXPORT template <typename... Ts, std::size_t... Is>
793
        HPX_HOST_DEVICE HPX_FORCEINLINE constexpr void next_iteration(
794
            hpx::tuple<Ts...>& args, hpx::util::index_pack<Is...>,
795
            std::size_t current_thread) noexcept
796
        {
797
            (hpx::get<Is>(args).next_iteration(current_thread), ...);
798
        }
799

800
        HPX_CXX_EXPORT template <typename... Ts, std::size_t... Is>
801
        HPX_HOST_DEVICE constexpr void exit_iteration(hpx::tuple<Ts...>& args,
802
            hpx::util::index_pack<Is...>, std::size_t size) noexcept
803
        {
804
            (hpx::get<Is>(args).exit_iteration(size), ...);
805
        }
806

807
        ///////////////////////////////////////////////////////////////////////
808
        HPX_CXX_EXPORT template <typename ExPolicy, typename F,
809
            typename S = void, typename Tuple = hpx::tuple<>>
810
        struct part_iterations;
811

812
        HPX_CXX_EXPORT template <typename ExPolicy, typename F, typename S,
813
            typename... Ts>
814
        struct part_iterations<ExPolicy, F, S, hpx::tuple<Ts...>>
815
        {
816
            using fun_type = std::decay_t<F>;
817

818
            fun_type f_;
819
            S stride_;
820
            hpx::tuple<Ts...> args_;
821

822
            part_iterations() = default;    // for serialization purposes only
823

824
            template <typename F_, typename S_, typename Args>
825
            part_iterations(F_&& f, S_&& stride, Args&& args)
826
              : f_(HPX_FORWARD(F_, f))
827
              , stride_(HPX_FORWARD(S_, stride))
828
              , args_(HPX_FORWARD(Args, args))
829
            {
830
            }
831

832
            template <typename B>
833
            HPX_HOST_DEVICE HPX_FORCEINLINE constexpr void operator()(
834
                B part_begin, std::size_t part_steps, std::size_t part_index)
835
            {
836
                std::size_t current_thread = -1;
837
                if constexpr (hpx::util::any_of_v<has_needs_current_thread_num<
838
                                  std::decay_t<Ts>...>>)
839
                {
840
                    current_thread = hpx::get_worker_thread_num();
841
                }
842

843
                auto pack = hpx::util::make_index_pack_t<sizeof...(Ts)>();
844
                detail::init_iteration(args_, pack, part_index, current_thread);
845

846
                if (stride_ == 1)
847
                {
848
                    while (part_steps-- != 0)
849
                    {
850
                        detail::invoke_iteration(
851
                            args_, pack, f_, part_begin++, current_thread);
852
                        detail::next_iteration(args_, pack, current_thread);
853
                    }
854
                }
855
                else if (stride_ > 0)
856
                {
857
                    while (part_steps >= static_cast<std::size_t>(stride_))
858
                    {
859
                        detail::invoke_iteration(
860
                            args_, pack, f_, part_begin, current_thread);
861

862
                        part_begin =
863
                            parallel::detail::next(part_begin, stride_);
864
                        part_steps -= stride_;
865

866
                        detail::next_iteration(args_, pack, current_thread);
867
                    }
868

869
                    if (part_steps != 0)
870
                    {
871
                        detail::invoke_iteration(
872
                            args_, pack, f_, part_begin, current_thread);
873
                        detail::next_iteration(args_, pack, current_thread);
874
                    }
875
                }
876
                else
877
                {
878
                    // Silence unary minus warning for unsigned types
879
                    if constexpr (std::is_signed_v<S>)
880
                    {
881
                        while (part_steps >= static_cast<std::size_t>(-stride_))
882
                        {
883
                            detail::invoke_iteration(
884
                                args_, pack, f_, part_begin, current_thread);
885

886
                            part_begin =
887
                                parallel::detail::next(part_begin, stride_);
888
                            part_steps += stride_;
889

890
                            detail::next_iteration(args_, pack, current_thread);
891
                        }
892

893
                        if (part_steps != 0)
894
                        {
895
                            detail::invoke_iteration(
896
                                args_, pack, f_, part_begin, current_thread);
897
                            detail::next_iteration(args_, pack, current_thread);
898
                        }
899
                    }
900
                    else
901
                    {
902
                        HPX_UNREACHABLE;
903
                    }
904
                }
905
            }
906

×
907
            template <typename Archive>
×
908
            void serialize(Archive& ar, unsigned)
×
909
            {
910
                // clang-format off
911
                ar & f_ & stride_ & args_;
912
                // clang-format on
913
            }
×
914
        };
×
915

×
916
        HPX_CXX_EXPORT template <typename ExPolicy, typename F, typename S>
917
        struct part_iterations<ExPolicy, F, S, hpx::tuple<>>
918
        {
919
            using fun_type = std::decay_t<F>;
920

921
            fun_type f_;
922
            S stride_;
923

924
            part_iterations() = default;    // for serialization purposes only
925

926
            template <typename F_,
927
                typename Enable = std::enable_if_t<
928
                    !std::is_same_v<std::decay_t<F_>, part_iterations>>>
929
            explicit part_iterations(F_&& f)
930
              : f_(HPX_FORWARD(F_, f))
931
              , stride_(1)
932
            {
×
933
            }
934

935
            template <typename F_, typename S_>
936
            part_iterations(F_&& f, S_&& stride)
937
              : f_(HPX_FORWARD(F_, f))
938
              , stride_(HPX_FORWARD(S_, stride))
939
            {
×
940
            }
941

942
            template <typename F_, typename S_, typename Args>
943
            part_iterations(F_&& f, S_&& stride, Args&&)
×
944
              : f_(HPX_FORWARD(F_, f))
945
              , stride_(HPX_FORWARD(S_, stride))
×
946
            {
947
            }
×
948

949
            template <typename B>
950
            HPX_HOST_DEVICE HPX_FORCEINLINE constexpr void operator()(
×
951
                B part_begin, std::size_t part_steps)
×
952
            {
953
                HPX_ASSERT(stride_ == 1);
954
                parallel::util::loop_n<std::decay_t<ExPolicy>>(
×
955
                    part_begin, part_steps, f_);
956
            }
×
957

958
            template <typename B>
959
            HPX_HOST_DEVICE HPX_FORCEINLINE constexpr void operator()(
960
                B part_begin, std::size_t part_steps, std::size_t)
961
            {
×
962
                if (stride_ == 1)
963
                {
×
964
                    (*this)(part_begin, part_steps);
965
                }
966
                else if (stride_ > 0)
×
967
                {
×
968
                    while (part_steps >= static_cast<std::size_t>(stride_))
969
                    {
970
                        HPX_INVOKE(f_, part_begin);
×
971

972
                        part_begin =
×
973
                            parallel::detail::next(part_begin, stride_);
974
                        part_steps -= stride_;
975
                    }
976

977
                    if (part_steps != 0)
978
                    {
979
                        HPX_INVOKE(f_, part_begin);
980
                    }
981
                }
982
                else
983
                {
984
                    // Silence unary minus warning for unsigned types
985
                    if constexpr (std::is_signed_v<S>)
986
                    {
987
                        while (part_steps >= static_cast<std::size_t>(-stride_))
988
                        {
989
                            HPX_INVOKE(f_, part_begin);
990

991
                            part_begin =
992
                                parallel::detail::next(part_begin, stride_);
993
                            part_steps += stride_;
994
                        }
995

996
                        if (part_steps != 0)
997
                        {
998
                            HPX_INVOKE(f_, part_begin);
999
                        }
1000
                    }
1001
                    else
1002
                    {
1003
                        HPX_UNREACHABLE;
1004
                    }
1005
                }
1006
            }
1007

1008
            template <typename Archive>
1009
            void serialize(Archive& ar, unsigned)
1010
            {
1011
                // clang-format off
1012
                ar & f_ & stride_;
1013
                // clang-format on
1014
            }
1015
        };
1016

1017
        HPX_CXX_EXPORT template <typename ExPolicy, typename F, typename... Ts>
1018
        struct part_iterations<ExPolicy, F, void, hpx::tuple<Ts...>>
1019
        {
1020
            using fun_type = std::decay_t<F>;
1021

1022
            fun_type f_;
1023
            hpx::tuple<Ts...> args_;
1024

1025
            part_iterations() = default;    // for serialization purposes only
1026

1027
            template <typename F_, typename Args>
1028
            part_iterations(F_&& f, Args&& args)
1029
              : f_(HPX_FORWARD(F_, f))
1030
              , args_(HPX_FORWARD(Args, args))
1031
            {
1032
            }
1033

1034
            template <typename B, typename E>
1035
            HPX_HOST_DEVICE HPX_FORCEINLINE constexpr void loop_iter(
1036
                B part_begin, E part_end, std::size_t part_index,
1037
                std::uint32_t current_thread)
1038
            {
1039
                auto pack = hpx::util::make_index_pack_t<sizeof...(Ts)>();
1040
                detail::init_iteration(args_, pack, part_index, current_thread);
1041

1042
                while (part_begin != part_end)
1043
                {
1044
                    detail::invoke_iteration(
1045
                        args_, pack, f_, part_begin++, current_thread);
1046
                    detail::next_iteration(args_, pack, current_thread);
1047
                }
1048
            }
1049

1050
            template <typename B>
1051
            HPX_HOST_DEVICE HPX_FORCEINLINE constexpr void operator()(
1052
                B part_begin, std::size_t part_steps,
1053
                std::size_t part_index = 0)
1054
            {
1055
                std::size_t current_thread = -1;
1056
                if constexpr (hpx::util::any_of_v<has_needs_current_thread_num<
1057
                                  std::decay_t<Ts>...>>)
1058
                {
1059
                    current_thread = hpx::get_worker_thread_num();
1060
                }
1061

1062
                if constexpr (hpx::traits::is_range_generator_v<B>)
1063
                {
1064
                    auto g = hpx::util::iterate(part_begin);
1065
                    loop_iter(hpx::util::begin(g), hpx::util::end(g),
1066
                        part_index, current_thread);
1067
                }
7✔
1068
                else if constexpr (hpx::traits::is_range_v<B>)
7✔
1069
                {
1070
                    loop_iter(hpx::util::begin(part_begin),
1071
                        hpx::util::end(part_begin), part_index, current_thread);
1072
                }
1073
                else
1074
                {
1075
                    auto pack = hpx::util::make_index_pack_t<sizeof...(Ts)>();
1076
                    detail::init_iteration(
1077
                        args_, pack, part_index, current_thread);
1078

1079
                    parallel::util::loop_n<std::decay_t<ExPolicy>>(
1080
                        part_begin, part_steps, [&](auto it) {
1081
                            detail::invoke_iteration(
1082
                                args_, pack, f_, it, current_thread);
1083
                            detail::next_iteration(args_, pack, current_thread);
1084
                        });
1085
                }
1086
            }
1087

1088
            template <typename Archive>
1089
            void serialize(Archive& ar, unsigned)
1090
            {
1091
                // clang-format off
1092
                ar & f_ & args_;
1093
                // clang-format on
1094
            }
1095
        };
1096

1097
        HPX_CXX_EXPORT template <typename ExPolicy, typename F>
1098
        struct part_iterations<ExPolicy, F, void, hpx::tuple<>>
1✔
1099
        {
1100
            using fun_type = std::decay_t<F>;
1101

1102
            fun_type f_;
1103

1104
            part_iterations() = default;    // for serialization purposes only
1105

1106
            template <typename F_,
1107
                typename Enable = std::enable_if_t<
1108
                    !std::is_same_v<std::decay_t<F_>, part_iterations>>>
1109
            explicit part_iterations(F_&& f)
1110
              : f_(HPX_FORWARD(F_, f))
1111
            {
1112
            }
1113

1114
            template <typename F_, typename Args>
1115
            part_iterations(F_&& f, Args&&)
1116
              : f_(HPX_FORWARD(F_, f))
1117
            {
1118
            }
1119

1120
            template <typename IterOrR>
2✔
1121
            HPX_HOST_DEVICE HPX_FORCEINLINE constexpr void operator()(
1122
                IterOrR part_begin, std::size_t part_steps, std::size_t = 0)
1123
            {
1124
                if constexpr (hpx::traits::is_range_generator_v<IterOrR>)
1125
                {
1126
                    auto g = hpx::util::iterate(part_begin);
2✔
1127
                    parallel::util::loop_ind<std::decay_t<ExPolicy>>(
1128
                        hpx::util::begin(g), hpx::util::end(g), f_);
1129
                }
1130
                else if constexpr (hpx::traits::is_range_v<IterOrR>)
1131
                {
1132
                    parallel::util::loop(hpx::util::begin(part_begin),
1133
                        hpx::util::end(part_begin), f_);
1134
                }
1135
                else
1136
                {
1137
                    static_assert(hpx::traits::is_iterator_v<IterOrR> ||
1138
                        std::is_integral_v<IterOrR>);
1139
                    parallel::util::loop_n<std::decay_t<ExPolicy>>(
1140
                        part_begin, part_steps, f_);
1141
                }
1142
            }
1143

1144
            template <typename Archive>
1145
            void serialize(Archive& ar, unsigned)
1146
            {
1147
                // clang-format off
1148
                ar & f_;
1149
                // clang-format on
1150
            }
1151
        };
1152

1153
        ///////////////////////////////////////////////////////////////////////
1154
        HPX_CXX_EXPORT struct for_loop_algo
1155
          : public detail::algorithm<for_loop_algo>
1✔
1156
        {
1157
            constexpr for_loop_algo() noexcept
1158
              : for_loop_algo::algorithm("for_loop_algo")
1159
            {
1160
            }
1161

1162
            template <typename ExPolicy, typename IterOrR, typename F>
1163
            HPX_HOST_DEVICE static constexpr hpx::util::unused_type sequential(
1✔
1164
                ExPolicy&&, IterOrR iter_or_r, std::size_t count, F&& f)
1165
            {
1166
                // perform iteration
1167
                auto iter = part_iterations<ExPolicy, F>{HPX_FORWARD(F, f)};
1168
                iter(iter_or_r, count);
1169
                return {};
1170
            }
1171

1172
            template <typename ExPolicy, typename IterOrR, typename Size,
1173
                typename F, typename Arg, typename... Args>
1174
            HPX_HOST_DEVICE static constexpr hpx::util::unused_type sequential(
1175
                ExPolicy&&, IterOrR iter_or_r, Size size, F&& f, Arg&& arg,
1176
                Args&&... args)
1177
            {
1178
                using args_type =
1179
                    hpx::tuple<std::decay_t<Arg>, std::decay_t<Args>...>;
1180
                args_type all_args = hpx::forward_as_tuple(
1181
                    HPX_FORWARD(Arg, arg), HPX_FORWARD(Args, args)...);
1182

1✔
1183
                // perform iteration
1184
                auto iter = part_iterations<ExPolicy, F, void, args_type>{
1185
                    HPX_FORWARD(F, f), all_args};
1186

1187
                iter(iter_or_r, size, 0);
1188

1189
                // make sure live-out variables are properly set on return
1190
                auto pack = hpx::util::make_index_pack_t<sizeof...(Args) + 1>();
1191
                exit_iteration(all_args, pack, size);
1192

1193
                return {};
1194
            }
1195

1196
            template <typename ExPolicy, typename IterOrR, typename Size,
1197
                typename F, typename... Ts>
1198
            static auto parallel(ExPolicy&& policy, IterOrR iter_or_r,
1199
                Size size, F&& f, Ts&&... ts)
1200
            {
1201
                constexpr bool is_scheduler_policy =
1202
                    hpx::execution_policy_has_scheduler_executor_v<ExPolicy>;
1203

1204
                if constexpr (!is_scheduler_policy)
1205
                {
1206
                    if (size == 0)
1207
                    {
1208
                        return util::detail::algorithm_result<ExPolicy>::get();
1209
                    }
1210
                }
1211

1212
                if constexpr (sizeof...(Ts) == 0)
1213
                {
1214
                    if constexpr (hpx::is_async_execution_policy_v<ExPolicy> ||
1215
                        is_scheduler_policy)
1216
                    {
1217
                        return util::detail::algorithm_result<ExPolicy>::get(
1218
                            util::partitioner<ExPolicy>::call(
1219
                                HPX_FORWARD(ExPolicy, policy), iter_or_r, size,
1220
                                part_iterations<ExPolicy, F>{HPX_FORWARD(F, f)},
1221
                                hpx::util::empty_function{}));
1222
                    }
1223
                    else
1224
                    {
1225
                        util::partitioner<ExPolicy>::call(
1226
                            HPX_FORWARD(ExPolicy, policy), iter_or_r, size,
1227
                            part_iterations<ExPolicy, F>{HPX_FORWARD(F, f)},
1228
                            hpx::util::empty_function{});
1229
                        return util::detail::algorithm_result<ExPolicy>::get();
1230
                    }
1231
                }
1232
                else
1233
                {
1234
                    // any of the induction or reduction operations prevent us
1235
                    // from sharing the part_iteration between threads
1236
                    decltype(auto) hinted_policy =
1237
                        hpx::execution::experimental::adapt_sharing_mode(
1238
                            HPX_FORWARD(ExPolicy, policy),
1239
                            hpx::threads::thread_sharing_hint::
1240
                                do_not_share_function);
1241

1242
                    using policy_type = std::decay_t<decltype(policy)>;
1243

1244
                    // we need to decay copy here to properly transport
1245
                    // everything to a GPU device
1246
                    using args_type = hpx::tuple<std::decay_t<Ts>...>;
1247

1248
                    args_type args =
1249
                        hpx::forward_as_tuple(HPX_FORWARD(Ts, ts)...);
1250

1251
                    return util::detail::algorithm_result<policy_type>::get(
1252
                        util::partitioner<policy_type>::call_with_index(
1253
                            hinted_policy, iter_or_r, size, 1,
1254
                            part_iterations<policy_type, F, void, args_type>{
1255
                                HPX_FORWARD(F, f), args},
1256
                            [=](auto&&) mutable {
1257
                                auto pack =
1258
                                    hpx::util::make_index_pack_t<sizeof...(
1259
                                        Ts)>();
1260
                                // make sure live-out variables are properly set on
1261
                                // return
1262
                                detail::exit_iteration(args, pack, size);
1263
                                return hpx::util::unused;
1264
                            }));
1265
                }
1266
            }
1267
        };    // namespace detail
1268

1269
        ///////////////////////////////////////////////////////////////////////
1270
        HPX_CXX_EXPORT struct for_loop_strided_algo
1271
          : public detail::algorithm<for_loop_strided_algo>
1272
        {
1273
            constexpr for_loop_strided_algo() noexcept
1274
              : for_loop_strided_algo::algorithm("for_loop_strided_algo")
1275
            {
1276
            }
1277

1278
            template <typename ExPolicy, typename InIter, typename S,
1279
                typename F>
1280
            HPX_HOST_DEVICE static constexpr hpx::util::unused_type sequential(
1281
                ExPolicy&&, InIter first, std::size_t count, S stride, F&& f)
1282
            {
1283
                if (stride == 1)
1284
                {
1285
                    parallel::util::loop_n<std::decay_t<ExPolicy>>(
1286
                        first, count, HPX_FORWARD(F, f));
1287
                }
1288
                else if (stride > 0)
1289
                {
1290
                    while (count >= static_cast<std::size_t>(stride))
1291
                    {
1292
                        HPX_INVOKE(f, first);
1293

1294
                        first = parallel::detail::next(first, stride);
1295
                        count -= stride;
1296
                    }
1297

1298
                    if (count != 0)
1299
                    {
1300
                        HPX_INVOKE(f, first);
1301
                    }
1302
                }
1303
                else
1304
                {
1305
                    while (count >= static_cast<std::size_t>(-stride))
1306
                    {
1307
                        HPX_INVOKE(f, first);
1308

1309
                        first = parallel::detail::next(first, stride);
1310
                        count += stride;
1311
                    }
1312

1313
                    if (count != 0)
1314
                    {
1315
                        HPX_INVOKE(f, first);
1316
                    }
1317
                }
1318

1319
                return {};
1320
            }
1321

1322
            template <typename ExPolicy, typename InIter, typename Size,
1323
                typename S, typename F, typename Arg, typename... Args>
1324
            HPX_HOST_DEVICE static constexpr hpx::util::unused_type sequential(
1325
                ExPolicy&&, InIter first, Size size, S stride, F&& f, Arg&& arg,
1326
                Args&&... args)
1327
            {
1328
                std::size_t current_thread = -1;
1329
                if constexpr (hpx::util::any_of_v<has_needs_current_thread_num<
1330
                                  std::decay_t<Args>...>>)
1331
                {
1332
                    current_thread = hpx::get_worker_thread_num();
1333
                }
×
1334

1335
                arg.init_iteration(0, current_thread);
1336
                (args.init_iteration(0, current_thread), ...);
1337

1338
                std::size_t count = size;
1339
                if (stride > 0)
1340
                {
1341
                    while (count >= static_cast<std::size_t>(stride))
×
1342
                    {
1343
                        HPX_INVOKE(f, first, arg.iteration_value(),
1344
                            args.iteration_value()...);
1345

1346
                        first = parallel::detail::next(first, stride);
1347
                        count -= stride;
1348

1349
                        arg.next_iteration(current_thread);
1350
                        (args.next_iteration(current_thread), ...);
1351
                    }
×
1352
                }
1353
                else
1354
                {
×
1355
                    while (count >= static_cast<std::size_t>(-stride))
1356
                    {
1357
                        HPX_INVOKE(f, first,
1358
                            arg.iteration_value(current_thread),
×
1359
                            args.iteration_value(current_thread)...);
1360

1361
                        first = parallel::detail::next(first, stride);
1362
                        count += stride;
1363

1364
                        arg.next_iteration(current_thread);
1365
                        (args.next_iteration(current_thread), ...);
1366
                    }
1367
                }
1368

1369
                if (count != 0)
1370
                {
1371
                    HPX_INVOKE(f, first, arg.iteration_value(current_thread),
1372
                        args.iteration_value(current_thread)...);
1373
                }
1374

1375
                // make sure live-out variables are properly set on return
×
1376
                arg.exit_iteration(size);
1377
                (args.exit_iteration(size), ...);
1378

1379
                return {};
×
1380
            }
1381

1382
            template <typename ExPolicy, typename B, typename Size, typename S,
1383
                typename F, typename... Ts>
1384
            static auto parallel(ExPolicy&& policy, B first, Size size,
1385
                S stride, F&& f, Ts&&... ts)
1386
            {
1387
                constexpr bool is_scheduler_policy =
1388
                    hpx::execution_policy_has_scheduler_executor_v<ExPolicy>;
1389

1390
                if constexpr (!is_scheduler_policy)
1391
                {
1392
                    if (size == 0)
1393
                    {
1394
                        return util::detail::algorithm_result<ExPolicy>::get();
1395
                    }
1396
                }
1397

1398
                if constexpr (sizeof...(Ts) == 0)
1399
                {
1400
                    if constexpr (!is_scheduler_policy)
1401
                    {
1402
                        if (stride == 1)
1403
                        {
1404
                            return util::detail::algorithm_result<ExPolicy>::
1405
                                get(util::partitioner<ExPolicy>::call(
1406
                                    HPX_FORWARD(ExPolicy, policy), first, size,
1407
                                    part_iterations<ExPolicy, F, S>{
1408
                                        HPX_FORWARD(F, f)},
1409
                                    [](auto&&) { return hpx::util::unused; }));
1410
                        }
1411
                    }
1412

1413
                    if constexpr (hpx::is_async_execution_policy_v<ExPolicy> ||
1414
                        is_scheduler_policy)
1415
                    {
1416
                        return util::detail::algorithm_result<ExPolicy>::get(
1417
                            util::partitioner<ExPolicy>::call_with_index(
1418
                                HPX_FORWARD(ExPolicy, policy), first, size,
1419
                                stride,
1420
                                part_iterations<ExPolicy, F, S>{
1421
                                    HPX_FORWARD(F, f), stride},
1422
                                [](auto&&) { return hpx::util::unused; }));
1423
                    }
1✔
1424
                    else
1425
                    {
1426
                        util::partitioner<ExPolicy>::call_with_index(
1427
                            HPX_FORWARD(ExPolicy, policy), first, size, stride,
1428
                            part_iterations<ExPolicy, F, S>{
1429
                                HPX_FORWARD(F, f), stride},
1430
                            [](auto&&) { return hpx::util::unused; });
1431
                        return util::detail::algorithm_result<ExPolicy>::get();
1432
                    }
1433
                }
1434
                else
1435
                {
1436
                    // any of the induction or reduction operations prevent us
1437
                    // from sharing the part_iteration between threads
1438
                    decltype(auto) hinted_policy =
1439
                        hpx::execution::experimental::adapt_sharing_mode(
1440
                            HPX_FORWARD(ExPolicy, policy),
1441
                            hpx::threads::thread_sharing_hint::
2✔
1442
                                do_not_share_function);
1443

1444
                    using policy_type = std::decay_t<decltype(policy)>;
1✔
1445

2✔
1446
                    // we need to decay copy here to properly transport
1✔
1447
                    // everything to a GPU device
1448
                    using args_type = hpx::tuple<std::decay_t<Ts>...>;
1449

1450
                    args_type args =
1451
                        hpx::forward_as_tuple(HPX_FORWARD(Ts, ts)...);
1452

1453
                    return util::detail::algorithm_result<policy_type>::get(
1454
                        util::partitioner<policy_type>::call_with_index(
1455
                            hinted_policy, first, size, stride,
1456
                            part_iterations<policy_type, F, S, args_type>{
1457
                                HPX_FORWARD(F, f), stride, args},
1458
                            [=](auto&&) mutable {
1459
                                auto pack =
1460
                                    hpx::util::make_index_pack_t<sizeof...(
1461
                                        Ts)>();
1462
                                // make sure live-out variables are properly set on
1463
                                // return
1464
                                detail::exit_iteration(args, pack, size);
1465
                                return hpx::util::unused;
1466
                            }));
1467
                }
1468
            }
1469
        };
1470

1471
        // reshuffle arguments, last argument is function object, will go first
1472
        HPX_CXX_EXPORT template <typename ExPolicy, typename B, typename E,
1473
            std::size_t... Is, typename... Args>
1474
        auto for_loop(ExPolicy&& policy, B first, E last,
1475
            hpx::util::index_pack<Is...>, Args&&... args)
1476
        {
1477
            if constexpr (std::is_integral_v<B> && std::is_signed_v<B> &&
1478
                std::is_integral_v<E> && std::is_signed_v<E> &&
1479
                !hpx::execution_policy_has_scheduler_executor_v<ExPolicy>)
1480
            {
1481
                if (first >= last)
1482
                {
1483
                    return util::detail::algorithm_result<ExPolicy>::get();
1484
                }
1485
            }
1486

1487
            static_assert(
1488
                std::is_integral_v<B> || hpx::traits::is_forward_iterator_v<B>,
1489
                "Requires at least forward iterator or integral loop "
1490
                "boundaries.");
1491

1492
            std::size_t size = parallel::detail::distance(first, last);
1493
            auto&& t = hpx::forward_as_tuple(HPX_FORWARD(Args, args)...);
1494

1495
            auto f = hpx::get<sizeof...(Args) - 1>(t);
1496
            return for_loop_algo().call(HPX_FORWARD(ExPolicy, policy), first,
1497
                size, HPX_MOVE(f), hpx::get<Is>(t)...);
1498
        }
1499

1500
        HPX_CXX_EXPORT template <typename ExPolicy, typename R,
1501
            std::size_t... Is, typename... Args>
1502
        auto for_loop_range(ExPolicy&& policy, R r,
1503
            hpx::util::index_pack<Is...>, Args&&... args)
1504
        {
1505
            constexpr bool scheduler_policy =
1506
                hpx::execution_policy_has_scheduler_executor_v<ExPolicy>;
1507

1508
            if constexpr (!scheduler_policy)
1509
            {
1510
                if (hpx::util::empty(r))
1511
                {
1512
                    return util::detail::algorithm_result<ExPolicy>::get();
1513
                }
1514
            }
1515

1516
            using iterator = hpx::traits::range_iterator_t<R>;
1517
            static_assert((hpx::traits::is_range_v<R> &&
×
1518
                              hpx::traits::is_forward_iterator_v<iterator>) ||
1519
                    (hpx::traits::is_range_generator_v<R> &&
1520
                        hpx::traits::is_input_iterator_v<iterator>),
1521
                "For ranges, requires at least forward iterator boundaries, "
1522
                "for range generators requires at least input iterator "
1523
                "boundaries.");
1524

1525
            std::size_t size = hpx::util::size(r);
1526
            auto&& t = hpx::forward_as_tuple(HPX_FORWARD(Args, args)...);
1527

1528
            auto f = hpx::get<sizeof...(Args) - 1>(t);
1529
            return for_loop_algo().call(HPX_FORWARD(ExPolicy, policy), r, size,
1530
                HPX_MOVE(f), hpx::get<Is>(t)...);
1531
        }
1532

1533
        // reshuffle arguments, last argument is function object, will go first
1534
        HPX_CXX_EXPORT template <typename ExPolicy, typename B, typename E,
1535
            typename S, std::size_t... Is, typename... Args>
1536
        auto for_loop_strided(ExPolicy&& policy, B first, E last, S stride,
1537
            hpx::util::index_pack<Is...>, Args&&... args)
1538
        {
1539
            // stride shall not be zero
1540
            HPX_ASSERT(stride != 0);
1541

1542
            if constexpr (std::is_integral_v<B> && std::is_signed_v<B> &&
1543
                std::is_integral_v<E> && std::is_signed_v<E> &&
1544
                !hpx::execution_policy_has_scheduler_executor_v<ExPolicy>)
1545
            {
1546
                if (first >= last)
1547
                {
1548
                    return util::detail::algorithm_result<ExPolicy>::get();
1549
                }
1550
            }
1551

1552
            // stride should be negative only if E is an integral type or at
1553
            // least a bidirectional iterator
1554
            if (stride < 0)
1555
            {
1556
                HPX_ASSERT(std::is_integral_v<E> ||
1557
                    hpx::traits::is_bidirectional_iterator_v<E>);
1558
            }
1559

1560
            static_assert(
1561
                std::is_integral_v<B> || hpx::traits::is_forward_iterator_v<B>,
1562
                "Requires at least forward iterator or integral loop "
1563
                "boundaries.");
1564

1565
            std::size_t size = parallel::detail::distance(first, last);
1566
            auto&& t = hpx::forward_as_tuple(HPX_FORWARD(Args, args)...);
1567

1568
            auto f = hpx::get<sizeof...(Args) - 1>(t);
1569
            return for_loop_strided_algo().call(HPX_FORWARD(ExPolicy, policy),
1570
                first, size, stride, HPX_MOVE(f), hpx::get<Is>(t)...);
1571
        }
1572

1573
        HPX_CXX_EXPORT template <typename ExPolicy, typename R, typename S,
1574
            std::size_t... Is, typename... Args>
1575
        auto for_loop_strided_range(ExPolicy&& policy, R r, S stride,
1576
            hpx::util::index_pack<Is...>, Args&&... args)
1577
        {
1578
            // stride shall not be zero
1579
            HPX_ASSERT(stride != 0);
1580

1581
            constexpr bool scheduler_policy =
1582
                hpx::execution_policy_has_scheduler_executor_v<ExPolicy>;
1583

1584
            if constexpr (!scheduler_policy)
1585
            {
1586
                if (hpx::util::empty(r))
1587
                {
1588
                    return util::detail::algorithm_result<ExPolicy>::get();
1589
                }
1590
            }
1591

1592
            // stride should be negative only if R exposes at least a
1593
            // bidirectional iterator
1594
            if (stride < 0)
1595
            {
1596
                HPX_ASSERT(hpx::traits::is_bidirectional_iterator_v<
1597
                    hpx::traits::range_category_t<R>>);
1598
            }
1599

1600
            static_assert(hpx::traits::is_forward_iterator_v<
1601
                              hpx::traits::range_category_t<R>>,
1602
                "Requires at least forward iterator or integral loop "
1603
                "boundaries.");
1604

1605
            std::size_t size = hpx::util::size(r);
1606
            auto&& t = hpx::forward_as_tuple(HPX_FORWARD(Args, args)...);
1607

1608
            auto f = hpx::get<sizeof...(Args) - 1>(t);
1609
            return for_loop_strided_algo().call(HPX_FORWARD(ExPolicy, policy),
1610
                r, size, stride, HPX_MOVE(f), hpx::get<Is>(t)...);
1611
        }
1612

1613
        // reshuffle arguments, last argument is function object, will go first
1614
        HPX_CXX_EXPORT template <typename ExPolicy, typename B, typename Size,
1615
            typename S, std::size_t... Is, typename... Args>
1616
        util::detail::algorithm_result_t<ExPolicy> for_loop_n(ExPolicy&& policy,
1✔
1617
            B first, Size size, S stride, hpx::util::index_pack<Is...>,
1618
            Args&&... args)
1619
        {
1✔
1620
            // stride shall not be zero
1621
            HPX_ASSERT(stride != 0);
1622

1623
            // stride should be negative only if E is an integral type or at
1624
            // least a bidirectional iterator
1625
            if (stride < 0)
1626
            {
1627
                HPX_ASSERT(std::is_integral_v<B> ||
1628
                    hpx::traits::is_bidirectional_iterator_v<B>);
2✔
1629
            }
1630

1631
            static_assert(
1632
                std::is_integral_v<B> || hpx::traits::is_forward_iterator_v<B>,
1633
                "Requires at least forward iterator or integral loop "
1634
                "boundaries.");
1635

6✔
1636
            auto&& t = hpx::forward_as_tuple(HPX_FORWARD(Args, args)...);
1637

2✔
1638
            auto f = hpx::get<sizeof...(Args) - 1>(t);
1639
            return for_loop_strided_algo().call(HPX_FORWARD(ExPolicy, policy),
1640
                first, size, stride, HPX_MOVE(f), hpx::get<Is>(t)...);
1641
        }
1642
        /// \endcond
1643
    }    // namespace detail
1644
}    // namespace hpx::parallel
1645

1646
namespace hpx::experimental {
1647

1648
    ///////////////////////////////////////////////////////////////////////////
1649
    HPX_CXX_EXPORT inline constexpr struct for_loop_t final
1650
      : hpx::detail::tag_parallel_algorithm<for_loop_t>
1651
    {
1652
    private:
1653
        template <typename ExPolicy, typename I, typename... Args>
1654
        // clang-format off
1655
        requires(
1656
            hpx::is_execution_policy_v<ExPolicy> &&
1657
            (hpx::traits::is_iterator_v<I> || std::is_integral_v<I>)
1658
        )
1659
        // clang-format on
1660
        friend decltype(auto) tag_fallback_invoke(hpx::experimental::for_loop_t,
1661
            ExPolicy&& policy, std::decay_t<I> first, I last, Args&&... args)
1662
        {
1663
            static_assert(sizeof...(Args) >= 1,
1664
                "for_loop must be called with at least a function object");
1665

1666
            using hpx::util::make_index_pack_t;
1667
            return hpx::parallel::detail::for_loop(
1668
                HPX_FORWARD(ExPolicy, policy), first, last,
1669
                make_index_pack_t<sizeof...(Args) - 1>(),
1670
                HPX_FORWARD(Args, args)...);
1671
        }
1672

1673
        template <typename I, typename... Args>
1674
            requires(hpx::traits::is_iterator_v<I> || std::is_integral_v<I>)
1675
        friend void tag_fallback_invoke(hpx::experimental::for_loop_t,
1676
            std::decay_t<I> first, I last, Args&&... args)
1677
        {
1678
            static_assert(sizeof...(Args) >= 1,
1679
                "for_loop must be called with at least a function object");
1680

1681
            using hpx::util::make_index_pack_t;
1682
            return hpx::parallel::detail::for_loop(hpx::execution::seq, first,
1683
                last, make_index_pack_t<sizeof...(Args) - 1>(),
1684
                HPX_FORWARD(Args, args)...);
1685
        }
1686
    } for_loop{};
1687

1688
    ///////////////////////////////////////////////////////////////////////////
1689
    HPX_CXX_EXPORT inline constexpr struct for_loop_strided_t final
1690
      : hpx::detail::tag_parallel_algorithm<for_loop_strided_t>
1691
    {
1692
    private:
1693
        template <typename ExPolicy, typename I, typename S, typename... Args>
1694
        // clang-format off
1695
        requires (
1696
            hpx::is_execution_policy_v<ExPolicy> &&
1697
            std::is_integral_v<S> &&
1698
            (hpx::traits::is_iterator_v<I> || std::is_integral_v<I>)
1699
        )
1700
        // clang-format on
1701
        friend hpx::parallel::util::detail::algorithm_result_t<ExPolicy>
1702
        tag_fallback_invoke(hpx::experimental::for_loop_strided_t,
1703
            ExPolicy&& policy, std::decay_t<I> first, I last, S stride,
1704
            Args&&... args)
1705
        {
1706
            static_assert(sizeof...(Args) >= 1,
1707
                "for_loop_strided must be called with at least a function "
1708
                "object");
1709

1710
            using hpx::util::make_index_pack_t;
1711
            return hpx::parallel::detail::for_loop_strided(
1712
                HPX_FORWARD(ExPolicy, policy), first, last, stride,
1713
                make_index_pack_t<sizeof...(Args) - 1>(),
1714
                HPX_FORWARD(Args, args)...);
1715
        }
1716

1717
        template <typename I, typename S, typename... Args>
1718
        // clang-format off
1719
        requires (
1720
            std::is_integral_v<S> &&
1721
            (hpx::traits::is_iterator_v<I> || std::is_integral_v<I>)
1722
        )
1723
        // clang-format on
1724
        friend void tag_fallback_invoke(hpx::experimental::for_loop_strided_t,
1725
            std::decay_t<I> first, I last, S stride, Args&&... args)
1726
        {
1727
            static_assert(sizeof...(Args) >= 1,
1728
                "for_loop_strided must be called with at least a function "
1729
                "object");
1730

1731
            using hpx::util::make_index_pack_t;
1732
            return hpx::parallel::detail::for_loop_strided(hpx::execution::seq,
1733
                first, last, stride, make_index_pack_t<sizeof...(Args) - 1>(),
1734
                HPX_FORWARD(Args, args)...);
1735
        }
1736
    } for_loop_strided{};
1737

1738
    ///////////////////////////////////////////////////////////////////////////
1739
    HPX_CXX_EXPORT inline constexpr struct for_loop_n_t final
1740
      : hpx::detail::tag_parallel_algorithm<for_loop_n_t>
1741
    {
1742
    private:
1743
        template <typename ExPolicy, typename I, typename Size,
1744
            typename... Args>
1745
        // clang-format off
1746
        requires (
1747
            hpx::is_execution_policy_v<ExPolicy> &&
1748
            std::is_integral_v<Size> &&
1749
            (hpx::traits::is_iterator_v<I> || std::is_integral_v<I>)
1750
        )
1751
        // clang-format on
1752
        friend hpx::parallel::util::detail::algorithm_result_t<ExPolicy>
1753
        tag_fallback_invoke(hpx::experimental::for_loop_n_t, ExPolicy&& policy,
1754
            I first, Size size, Args&&... args)
1755
        {
1756
            static_assert(sizeof...(Args) >= 1,
1757
                "for_loop_n must be called with at least a function object");
1758

1759
            using hpx::util::make_index_pack_t;
1760
            return hpx::parallel::detail::for_loop_n(
1761
                HPX_FORWARD(ExPolicy, policy), first, size, 1,
1762
                make_index_pack_t<sizeof...(Args) - 1>(),
1763
                HPX_FORWARD(Args, args)...);
1764
        }
1765

1766
        template <typename I, typename Size, typename... Args>
1767
        // clang-format off
1768
        requires (
1769
            std::is_integral_v<Size> &&
1770
            (hpx::traits::is_iterator_v<I> || std::is_integral_v<I>)
1771
        )
1772
        // clang-format on
1773
        friend void tag_fallback_invoke(
1774
            hpx::experimental::for_loop_n_t, I first, Size size, Args&&... args)
1775
        {
1776
            static_assert(sizeof...(Args) >= 1,
1777
                "for_loop_n must be called with at least a function object");
1778

1779
            using hpx::util::make_index_pack_t;
1780
            return hpx::parallel::detail::for_loop_n(hpx::execution::seq, first,
1781
                size, 1, make_index_pack_t<sizeof...(Args) - 1>(),
1782
                HPX_FORWARD(Args, args)...);
1783
        }
1784
    } for_loop_n{};
1785

1786
    ///////////////////////////////////////////////////////////////////////////
1787
    HPX_CXX_EXPORT inline constexpr struct for_loop_n_strided_t final
1788
      : hpx::detail::tag_parallel_algorithm<for_loop_n_strided_t>
1789
    {
1790
    private:
1791
        template <typename ExPolicy, typename I, typename Size, typename S,
1792
            typename... Args>
1793
        // clang-format off
1794
        requires (
1795
            hpx::is_execution_policy_v<ExPolicy> &&
1796
            std::is_integral_v<Size> &&
1797
            std::is_integral_v<S> &&
1798
            (hpx::traits::is_iterator_v<I> || std::is_integral_v<I>)
1799
        )
1800
        // clang-format on
1801
        friend hpx::parallel::util::detail::algorithm_result_t<ExPolicy>
1802
        tag_fallback_invoke(hpx::experimental::for_loop_n_strided_t,
1803
            ExPolicy&& policy, I first, Size size, S stride, Args&&... args)
1804
        {
1805
            static_assert(sizeof...(Args) >= 1,
1806
                "for_loop_n_strided must be called with at least a function "
1807
                "object");
1808

1809
            using hpx::util::make_index_pack_t;
1810
            return hpx::parallel::detail::for_loop_n(
1811
                HPX_FORWARD(ExPolicy, policy), first, size, stride,
1812
                make_index_pack_t<sizeof...(Args) - 1>(),
1813
                HPX_FORWARD(Args, args)...);
1814
        }
1815

1816
        template <typename I, typename Size, typename S, typename... Args>
1817
        // clang-format off
1818
        requires (
1819
            std::is_integral_v<Size> &&
1820
            std::is_integral_v<S> &&
1821
            (hpx::traits::is_iterator_v<I> || std::is_integral_v<I>)
1822
        )
1823
        // clang-format on
1824
        friend void tag_fallback_invoke(hpx::experimental::for_loop_n_strided_t,
1825
            I first, Size size, S stride, Args&&... args)
1826
        {
1827
            static_assert(sizeof...(Args) >= 1,
1828
                "for_loop_n_strided must be called with at least a function "
1829
                "object");
1830

1831
            using hpx::util::make_index_pack_t;
1832
            return hpx::parallel::detail::for_loop_n(hpx::execution::seq, first,
1833
                size, stride, make_index_pack_t<sizeof...(Args) - 1>(),
1834
                HPX_FORWARD(Args, args)...);
1835
        }
1836
    } for_loop_n_strided{};
1837
}    // namespace hpx::experimental
1838

1839
#if defined(HPX_HAVE_THREAD_DESCRIPTION)
1840
namespace hpx::traits {
1841

1842
    HPX_CXX_EXPORT template <typename ExPolicy, typename F, typename S,
1843
        typename Tuple>
1844
    struct get_function_address<
1845
        hpx::parallel::detail::part_iterations<ExPolicy, F, S, Tuple>>
1846
    {
1847
        static constexpr std::size_t call(
1848
            hpx::parallel::detail::part_iterations<ExPolicy, F, S, Tuple> const&
1849
                f) noexcept
1850
        {
1851
            return get_function_address<std::decay_t<F>>::call(f.f_);
1852
        }
1853
    };
1854

1855
    HPX_CXX_EXPORT template <typename ExPolicy, typename F, typename S,
1856
        typename Tuple>
1857
    struct get_function_annotation<
1858
        hpx::parallel::detail::part_iterations<ExPolicy, F, S, Tuple>>
1859
    {
1860
        static constexpr char const* call(
1861
            hpx::parallel::detail::part_iterations<ExPolicy, F, S, Tuple> const&
1862
                f) noexcept
1863
        {
1864
            return get_function_annotation<std::decay_t<F>>::call(f.f_);
1865
        }
1866
    };
1867

1868
#if HPX_HAVE_ITTNOTIFY != 0 && !defined(HPX_HAVE_APEX)
1869
    HPX_CXX_EXPORT template <typename ExPolicy, typename F, typename S,
1870
        typename Tuple>
1871
    struct get_function_annotation_itt<
1872
        hpx::parallel::detail::part_iterations<ExPolicy, F, S, Tuple>>
1873
    {
1874
        static util::itt::string_handle call(
1875
            hpx::parallel::detail::part_iterations<ExPolicy, F, S, Tuple> const&
1876
                f) noexcept
1877
        {
1878
            return get_function_annotation_itt<std::decay_t<F>>::call(f.f_);
1879
        }
1880
    };
1881
#endif
1882
}    // namespace hpx::traits
1883
#endif
1884
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc