2 //===-- utils.h -----------------------------------------------------------===//
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8 //===----------------------------------------------------------------------===//
10 // File contains common utilities that tests rely on
12 // Do not #include <algorithm>, because if we do we will not detect accidental dependencies.
23 #include "pstl_test_config.h"
28 typedef double float64_t
;
29 typedef float float32_t
;
31 template <class T
, std::size_t N
>
33 const_size(const T (&)[N
]) noexcept
41 // Handy macros for error reporting
42 #define EXPECT_TRUE(condition, message) ::TestUtils::expect(true, condition, __FILE__, __LINE__, message)
43 #define EXPECT_FALSE(condition, message) ::TestUtils::expect(false, condition, __FILE__, __LINE__, message)
45 // Check that expected and actual are equal and have the same type.
46 #define EXPECT_EQ(expected, actual, message) ::TestUtils::expect_equal(expected, actual, __FILE__, __LINE__, message)
48 // Check that sequences started with expected and actual and have had size n are equal and have the same type.
49 #define EXPECT_EQ_N(expected, actual, n, message) \
50 ::TestUtils::expect_equal(expected, actual, n, __FILE__, __LINE__, message)
52 // Issue error message from outstr, adding a newline.
53 // Real purpose of this routine is to have a place to hang a breakpoint.
55 issue_error_message(std::stringstream
& outstr
)
58 std::cerr
<< outstr
.str();
59 std::exit(EXIT_FAILURE
);
63 expect(bool expected
, bool condition
, const char* file
, int32_t line
, const char* message
)
65 if (condition
!= expected
)
67 std::stringstream outstr
;
68 outstr
<< "error at " << file
<< ":" << line
<< " - " << message
;
69 issue_error_message(outstr
);
73 // Do not change signature to const T&.
74 // Function must be able to detect const differences between expected and actual.
77 expect_equal(T
& expected
, T
& actual
, const char* file
, int32_t line
, const char* message
)
79 if (!(expected
== actual
))
81 std::stringstream outstr
;
82 outstr
<< "error at " << file
<< ":" << line
<< " - " << message
<< ", expected " << expected
<< " got "
84 issue_error_message(outstr
);
90 expect_equal(Sequence
<T
>& expected
, Sequence
<T
>& actual
, const char* file
, int32_t line
, const char* message
)
92 size_t n
= expected
.size();
93 size_t m
= actual
.size();
96 std::stringstream outstr
;
97 outstr
<< "error at " << file
<< ":" << line
<< " - " << message
<< ", expected sequence of size " << n
98 << " got sequence of size " << m
;
99 issue_error_message(outstr
);
102 size_t error_count
= 0;
103 for (size_t k
= 0; k
< n
&& error_count
< 10; ++k
)
105 if (!(expected
[k
] == actual
[k
]))
107 std::stringstream outstr
;
108 outstr
<< "error at " << file
<< ":" << line
<< " - " << message
<< ", at index " << k
<< " expected "
109 << expected
[k
] << " got " << actual
[k
];
110 issue_error_message(outstr
);
116 template <typename Iterator1
, typename Iterator2
, typename Size
>
118 expect_equal(Iterator1 expected_first
, Iterator2 actual_first
, Size n
, const char* file
, int32_t line
,
121 size_t error_count
= 0;
122 for (Size k
= 0; k
< n
&& error_count
< 10; ++k
, ++expected_first
, ++actual_first
)
124 if (!(*expected_first
== *actual_first
))
126 std::stringstream outstr
;
127 outstr
<< "error at " << file
<< ":" << line
<< " - " << message
<< ", at index " << k
;
128 issue_error_message(outstr
);
134 // ForwardIterator is like type Iterator, but restricted to be a forward iterator.
135 // Only the forward iterator signatures that are necessary for tests are present.
136 // Post-increment in particular is deliberatly omitted since our templates should avoid using it
137 // because of efficiency considerations.
138 template <typename Iterator
, typename IteratorTag
>
139 class ForwardIterator
142 typedef IteratorTag iterator_category
;
143 typedef typename
std::iterator_traits
<Iterator
>::value_type value_type
;
144 typedef typename
std::iterator_traits
<Iterator
>::difference_type difference_type
;
145 typedef typename
std::iterator_traits
<Iterator
>::pointer pointer
;
146 typedef typename
std::iterator_traits
<Iterator
>::reference reference
;
149 Iterator my_iterator
;
150 typedef value_type element_type
;
153 ForwardIterator() = default;
154 explicit ForwardIterator(Iterator i
) : my_iterator(i
) {}
155 reference
operator*() const { return *my_iterator
; }
156 Iterator
operator->() const { return my_iterator
; }
163 ForwardIterator
operator++(int32_t)
170 operator==(const ForwardIterator
& i
, const ForwardIterator
& j
)
172 return i
.my_iterator
== j
.my_iterator
;
175 operator!=(const ForwardIterator
& i
, const ForwardIterator
& j
)
177 return i
.my_iterator
!= j
.my_iterator
;
187 template <typename Iterator
, typename IteratorTag
>
188 class BidirectionalIterator
: public ForwardIterator
<Iterator
, IteratorTag
>
190 typedef ForwardIterator
<Iterator
, IteratorTag
> base_type
;
193 BidirectionalIterator() = default;
194 explicit BidirectionalIterator(Iterator i
) : base_type(i
) {}
195 BidirectionalIterator(const base_type
& i
) : base_type(i
.iterator()) {}
197 BidirectionalIterator
200 ++base_type::my_iterator
;
203 BidirectionalIterator
206 --base_type::my_iterator
;
209 BidirectionalIterator
operator++(int32_t)
212 base_type::my_iterator
++;
215 BidirectionalIterator
operator--(int32_t)
218 base_type::my_iterator
--;
223 template <typename Iterator
, typename F
>
225 fill_data(Iterator first
, Iterator last
, F f
)
227 typedef typename
std::iterator_traits
<Iterator
>::value_type T
;
228 for (std::size_t i
= 0; first
!= last
; ++first
, ++i
)
234 struct MemoryChecker
{
235 // static counters and state tags
236 static std::atomic
<std::int64_t> alive_object_counter
; // initialized outside
237 static constexpr std::int64_t alive_state
= 0xAAAAAAAAAAAAAAAA;
238 static constexpr std::int32_t dead_state
= 0; // only used as a set value to cancel alive_state
240 std::int32_t _value
; // object value used for algorithms
241 std::int64_t _state
; // state tag used for checks
243 // ctors, dtors, assign ops
244 explicit MemoryChecker(std::int32_t value
= 0) : _value(value
) {
245 // check for EXPECT_TRUE(state() != alive_state, ...) has not been done since we cannot guarantee that
246 // raw memory for object being constructed does not have a bit sequence being equal to alive_state
248 // set constructed state and increment counter for living object
250 _state
= alive_state
;
252 MemoryChecker(MemoryChecker
&& other
) : _value(other
.value()) {
253 // check for EXPECT_TRUE(state() != alive_state, ...) has not been done since
254 // compiler can optimize out the move ctor call that results in false positive failure
255 EXPECT_TRUE(other
.state() == alive_state
, "wrong effect from MemoryChecker(MemoryChecker&&): attemp to construct an object from non-existing object");
256 // set constructed state and increment counter for living object
258 _state
= alive_state
;
260 MemoryChecker(const MemoryChecker
& other
) : _value(other
.value()) {
261 // check for EXPECT_TRUE(state() != alive_state, ...) has not been done since
262 // compiler can optimize out the copy ctor call that results in false positive failure
263 EXPECT_TRUE(other
.state() == alive_state
, "wrong effect from MemoryChecker(const MemoryChecker&): attemp to construct an object from non-existing object");
264 // set constructed state and increment counter for living object
266 _state
= alive_state
;
268 MemoryChecker
& operator=(MemoryChecker
&& other
) {
269 // check if we do not assign over uninitialized memory
270 EXPECT_TRUE(state() == alive_state
, "wrong effect from MemoryChecker::operator=(MemoryChecker&& other): attemp to assign to non-existing object");
271 EXPECT_TRUE(other
.state() == alive_state
, "wrong effect from MemoryChecker::operator=(MemoryChecker&& other): attemp to assign from non-existing object");
272 // just assign new value, counter is the same, state is the same
273 _value
= other
.value();
277 MemoryChecker
& operator=(const MemoryChecker
& other
) {
278 // check if we do not assign over uninitialized memory
279 EXPECT_TRUE(state() == alive_state
, "wrong effect from MemoryChecker::operator=(const MemoryChecker& other): attemp to assign to non-existing object");
280 EXPECT_TRUE(other
.state() == alive_state
, "wrong effect from MemoryChecker::operator=(const MemoryChecker& other): attemp to assign from non-existing object");
281 // just assign new value, counter is the same, state is the same
282 _value
= other
.value();
287 // check if we do not double destruct the object
288 EXPECT_TRUE(state() == alive_state
, "wrong effect from ~MemoryChecker(): attemp to destroy non-existing object");
289 // set destructed state and decrement counter for living object
290 static_cast<volatile std::int64_t&>(_state
) = dead_state
;
295 std::int32_t value() const { return _value
; }
296 std::int64_t state() const { return _state
; }
297 static std::int32_t alive_objects() { return alive_object_counter
.load(); }
300 void inc_alive_objects() { alive_object_counter
.fetch_add(1); }
301 void dec_alive_objects() { alive_object_counter
.fetch_sub(1); }
304 std::atomic
<std::int64_t> MemoryChecker::alive_object_counter
{0};
306 std::ostream
& operator<<(std::ostream
& os
, const MemoryChecker
& val
) { return (os
<< val
.value()); }
307 bool operator==(const MemoryChecker
& v1
, const MemoryChecker
& v2
) { return v1
.value() == v2
.value(); }
308 bool operator<(const MemoryChecker
& v1
, const MemoryChecker
& v2
) { return v1
.value() < v2
.value(); }
310 // Sequence<T> is a container of a sequence of T with lots of kinds of iterators.
311 // Prefixes on begin/end mean:
314 // No prefix indicates non-const random-access iterator.
315 template <typename T
>
318 std::vector
<T
> m_storage
;
321 typedef typename
std::vector
<T
>::iterator iterator
;
322 typedef typename
std::vector
<T
>::const_iterator const_iterator
;
323 typedef ForwardIterator
<iterator
, std::forward_iterator_tag
> forward_iterator
;
324 typedef ForwardIterator
<const_iterator
, std::forward_iterator_tag
> const_forward_iterator
;
326 typedef BidirectionalIterator
<iterator
, std::bidirectional_iterator_tag
> bidirectional_iterator
;
327 typedef BidirectionalIterator
<const_iterator
, std::bidirectional_iterator_tag
> const_bidirectional_iterator
;
329 typedef T value_type
;
330 explicit Sequence(size_t size
) : m_storage(size
) {}
332 // Construct sequence [f(0), f(1), ... f(size-1)]
333 // f can rely on its invocations being sequential from 0 to size-1.
334 template <typename Func
>
335 Sequence(size_t size
, Func f
)
337 m_storage
.reserve(size
);
338 // Use push_back because T might not have a default constructor
339 for (size_t k
= 0; k
< size
; ++k
)
340 m_storage
.push_back(T(f(k
)));
342 Sequence(const std::initializer_list
<T
>& data
) : m_storage(data
) {}
347 return m_storage
.begin();
352 return m_storage
.end();
357 return m_storage
.begin();
362 return m_storage
.end();
367 return m_storage
.cbegin();
372 return m_storage
.cend();
377 return forward_iterator(m_storage
.begin());
382 return forward_iterator(m_storage
.end());
384 const_forward_iterator
387 return const_forward_iterator(m_storage
.cbegin());
389 const_forward_iterator
392 return const_forward_iterator(m_storage
.cend());
394 const_forward_iterator
397 return const_forward_iterator(m_storage
.cbegin());
399 const_forward_iterator
402 return const_forward_iterator(m_storage
.cend());
405 const_bidirectional_iterator
408 return const_bidirectional_iterator(m_storage
.cbegin());
410 const_bidirectional_iterator
413 return const_bidirectional_iterator(m_storage
.cend());
416 bidirectional_iterator
419 return bidirectional_iterator(m_storage
.begin());
421 bidirectional_iterator
424 return bidirectional_iterator(m_storage
.end());
430 return m_storage
.size();
435 return m_storage
.data();
437 typename
std::vector
<T
>::reference
operator[](size_t j
) { return m_storage
[j
]; }
438 const T
& operator[](size_t j
) const { return m_storage
[j
]; }
440 // Fill with given value
444 for (size_t i
= 0; i
< m_storage
.size(); i
++)
445 m_storage
[i
] = value
;
451 template <typename Func
>
455 fill_data(m_storage
.begin(), m_storage
.end(), f
);
459 template <typename T
>
461 Sequence
<T
>::print() const
463 std::cout
<< "size = " << size() << ": { ";
464 std::copy(begin(), end(), std::ostream_iterator
<T
>(std::cout
, " "));
465 std::cout
<< " } " << std::endl
;
468 // Predicates for algorithms
469 template <typename DataType
>
472 is_equal_to(const DataType
& expected
) : m_expected(expected
) {}
474 operator()(const DataType
& actual
) const
476 return actual
== m_expected
;
483 // Low-quality hash function, returns value between 0 and (1<<bits)-1
484 // Warning: low-order bits are quite predictable.
486 HashBits(size_t i
, size_t bits
)
488 size_t mask
= bits
>= 8 * sizeof(size_t) ? ~size_t(0) : (size_t(1) << bits
) - 1;
489 return (424157 * i
^ 0x24aFa) & mask
;
493 template <typename T
, typename U
>
499 Complement(T v
) : val(v
) {}
501 operator()(const T
& x
) const
507 // Tag used to prevent accidental use of converting constructor, even if use is explicit.
514 // Type with limited set of operations. Not default-constructible.
515 // Only available operator is "==".
516 // Typically used as value type in tests.
522 friend class IsMultiple
;
523 friend class Congruent
;
525 operator+(const Sum
& x
, const Sum
& y
);
528 Number(int32_t val
, OddTag
) : value(val
) {}
530 operator==(const Number
& x
, const Number
& y
)
532 return x
.value
== y
.value
;
535 operator<<(std::ostream
& o
, const Number
& d
)
541 // Stateful predicate for Number. Not default-constructible.
547 // True if x is multiple of modulus
549 operator()(Number x
) const
551 return x
.value
% modulus
== 0;
553 IsMultiple(long modulus_
, OddTag
) : modulus(modulus_
) {}
556 // Stateful equivalence-class predicate for Number. Not default-constructible.
562 // True if x and y have same remainder for the given modulus.
563 // Note: this is not quite the same as "equivalent modulo modulus" when x and y have different
564 // sign, but nonetheless AreCongruent is still an equivalence relationship, which is all
565 // we need for testing.
567 operator()(Number x
, Number y
) const
569 return x
.value
% modulus
== y
.value
% modulus
;
571 Congruent(long modulus_
, OddTag
) : modulus(modulus_
) {}
574 // Stateful reduction operation for Number
580 explicit Add(OddTag
) : bias(1) {}
582 operator()(Number x
, const Number
& y
)
584 return Number(x
.value
+ y
.value
+ (bias
- 1), OddTag());
588 // Class similar to Number, but has default constructor and +.
589 class Sum
: public Number
592 Sum() : Number(0, OddTag()) {}
593 Sum(long x
, OddTag
) : Number(x
, OddTag()) {}
595 operator+(const Sum
& x
, const Sum
& y
)
597 return Sum(x
.value
+ y
.value
, OddTag());
601 // Type with limited set of operations, which includes an associative but not commutative operation.
602 // Not default-constructible.
603 // Typically used as value type in tests involving "GENERALIZED_NONCOMMUTATIVE_SUM".
609 MonoidElement(size_t a_
, size_t b_
, OddTag
) : a(a_
), b(b_
) {}
611 operator==(const MonoidElement
& x
, const MonoidElement
& y
)
613 return x
.a
== y
.a
&& x
.b
== y
.b
;
616 operator<<(std::ostream
& o
, const MonoidElement
& x
)
618 return o
<< "[" << x
.a
<< ".." << x
.b
<< ")";
620 friend class AssocOp
;
623 // Stateful associative op for MonoidElement
624 // It's not really a monoid since the operation is not allowed for any two elements.
625 // But it's good enough for testing.
631 explicit AssocOp(OddTag
) : c(5) {}
633 operator()(const MonoidElement
& x
, const MonoidElement
& y
)
636 EXPECT_EQ(d
, c
, "state lost");
637 EXPECT_EQ(x
.b
, y
.a
, "commuted?");
639 return MonoidElement(x
.a
, y
.b
, OddTag());
643 // Multiplication of matrix is an associative but not commutative operation
644 // Typically used as value type in tests involving "GENERALIZED_NONCOMMUTATIVE_SUM".
645 template <typename T
>
649 Matrix2x2() : a
{{1, 0}, {0, 1}} {}
650 Matrix2x2(T x
, T y
) : a
{{0, x
}, {x
, y
}} {}
651 #if !defined(_PSTL_ICL_19_VC14_VC141_TEST_SCAN_RELEASE_BROKEN)
652 Matrix2x2(const Matrix2x2
& m
) : a
{{m
.a
[0][0], m
.a
[0][1]}, {m
.a
[1][0], m
.a
[1][1]}} {}
654 operator=(const Matrix2x2
& m
)
656 a
[0][0] = m
.a
[0][0], a
[0][1] = m
.a
[0][1], a
[1][0] = m
.a
[1][0], a
[1][1] = m
.a
[1][1];
662 template <typename T
>
664 operator==(const Matrix2x2
<T
>& left
, const Matrix2x2
<T
>& right
)
666 return left
.a
[0][0] == right
.a
[0][0] && left
.a
[0][1] == right
.a
[0][1] && left
.a
[1][0] == right
.a
[1][0] &&
667 left
.a
[1][1] == right
.a
[1][1];
670 template <typename T
>
672 multiply_matrix(const Matrix2x2
<T
>& left
, const Matrix2x2
<T
>& right
)
675 for (int32_t i
= 0; i
< 2; ++i
)
677 for (int32_t j
= 0; j
< 2; ++j
)
679 result
.a
[i
][j
] = left
.a
[i
][0] * right
.a
[0][j
] + left
.a
[i
][1] * right
.a
[1][j
];
685 //============================================================================
686 // Adapters for creating different types of iterators.
688 // In this block we implemented some adapters for creating differnet types of iterators.
689 // It's needed for extending the unit testing of Parallel STL algorithms.
690 // We have adapters for iterators with different tags (forward_iterator_tag, bidirectional_iterator_tag), reverse iterators.
691 // The input iterator should be const or non-const, non-reverse random access iterator.
692 // Iterator creates in "MakeIterator":
693 // firstly, iterator is "packed" by "IteratorTypeAdapter" (creating forward or bidirectional iterator)
694 // then iterator is "packed" by "ReverseAdapter" (if it's possible)
695 // So, from input iterator we may create, for example, reverse bidirectional iterator.
696 // "Main" functor for testing iterators is named "invoke_on_all_iterator_types".
699 template <typename Iterator
>
702 typedef Iterator iterator_type
;
704 operator()(Iterator it
)
710 // Check if the iterator is reverse iterator
711 // Note: it works only for iterators that created by std::reverse_iterator
712 template <typename NotReverseIterator
>
713 struct isReverse
: std::false_type
717 template <typename Iterator
>
718 struct isReverse
<std::reverse_iterator
<Iterator
>> : std::true_type
723 template <typename Iterator
, typename IsReverse
>
724 struct ReverseAdapter
726 typedef std::reverse_iterator
<Iterator
> iterator_type
;
728 operator()(Iterator it
)
730 #if defined(_PSTL_CPP14_MAKE_REVERSE_ITERATOR_PRESENT)
731 return std::make_reverse_iterator(it
);
733 return iterator_type(it
);
738 // Non-reverse adapter
739 template <typename Iterator
>
740 struct ReverseAdapter
<Iterator
, std::false_type
> : BaseAdapter
<Iterator
>
744 // Iterator adapter by type (by default std::random_access_iterator_tag)
745 template <typename Iterator
, typename IteratorTag
>
746 struct IteratorTypeAdapter
: BaseAdapter
<Iterator
>
750 // Iterator adapter for forward iterator
751 template <typename Iterator
>
752 struct IteratorTypeAdapter
<Iterator
, std::forward_iterator_tag
>
754 typedef ForwardIterator
<Iterator
, std::forward_iterator_tag
> iterator_type
;
756 operator()(Iterator it
)
758 return iterator_type(it
);
762 // Iterator adapter for bidirectional iterator
763 template <typename Iterator
>
764 struct IteratorTypeAdapter
<Iterator
, std::bidirectional_iterator_tag
>
766 typedef BidirectionalIterator
<Iterator
, std::bidirectional_iterator_tag
> iterator_type
;
768 operator()(Iterator it
)
770 return iterator_type(it
);
774 //For creating iterator with new type
775 template <typename InputIterator
, typename IteratorTag
, typename IsReverse
>
778 typedef IteratorTypeAdapter
<InputIterator
, IteratorTag
> IterByType
;
779 typedef ReverseAdapter
<typename
IterByType::iterator_type
, IsReverse
> ReverseIter
;
781 typename
ReverseIter::iterator_type
782 operator()(InputIterator it
)
784 return ReverseIter()(IterByType()(it
));
788 // Useful constant variables
789 constexpr std::size_t GuardSize
= 5;
790 constexpr std::ptrdiff_t sizeLimit
= 1000;
792 template <typename Iter
, typename Void
= void> // local iterator_traits for non-iterators
793 struct iterator_traits_
797 template <typename Iter
> // For iterators
798 struct iterator_traits_
<Iter
,
799 typename
std::enable_if
<!std::is_void
<typename
Iter::iterator_category
>::value
, void>::type
>
801 typedef typename
Iter::iterator_category iterator_category
;
804 template <typename T
> // For pointers
805 struct iterator_traits_
<T
*>
807 typedef std::random_access_iterator_tag iterator_category
;
810 // is iterator Iter has tag Tag
811 template <typename Iter
, typename Tag
>
812 using is_same_iterator_category
= std::is_same
<typename iterator_traits_
<Iter
>::iterator_category
, Tag
>;
814 // if we run with reverse or const iterators we shouldn't test the large range
815 template <typename IsReverse
, typename IsConst
>
818 template <typename Op
, typename
... Rest
>
820 operator()(bool is_allow
, Op op
, Rest
&&... rest
)
823 op(std::forward
<Rest
>(rest
)...);
827 struct invoke_if_
<std::false_type
, std::false_type
>
829 template <typename Op
, typename
... Rest
>
831 operator()(bool, Op op
, Rest
&&... rest
)
833 op(std::forward
<Rest
>(rest
)...);
837 // Base non_const_wrapper struct. It is used to distinguish non_const testcases
838 // from a regular one. For non_const testcases only compilation is checked.
839 struct non_const_wrapper
843 // Generic wrapper to specify iterator type to execute callable Op on.
844 // The condition can be either positive(Op is executed only with IteratorTag)
845 // or negative(Op is executed with every type of iterators except IteratorTag)
846 template <typename Op
, typename IteratorTag
, bool IsPositiveCondition
= true>
847 struct non_const_wrapper_tagged
: non_const_wrapper
849 template <typename Policy
, typename Iterator
>
850 typename
std::enable_if
<IsPositiveCondition
== is_same_iterator_category
<Iterator
, IteratorTag
>::value
, void>::type
851 operator()(Policy
&& exec
, Iterator iter
)
856 template <typename Policy
, typename InputIterator
, typename OutputIterator
>
857 typename
std::enable_if
<IsPositiveCondition
== is_same_iterator_category
<OutputIterator
, IteratorTag
>::value
,
859 operator()(Policy
&& exec
, InputIterator input_iter
, OutputIterator out_iter
)
861 Op()(exec
, input_iter
, out_iter
);
864 template <typename Policy
, typename Iterator
>
865 typename
std::enable_if
<IsPositiveCondition
!= is_same_iterator_category
<Iterator
, IteratorTag
>::value
, void>::type
866 operator()(Policy
&&, Iterator
)
870 template <typename Policy
, typename InputIterator
, typename OutputIterator
>
871 typename
std::enable_if
<IsPositiveCondition
!= is_same_iterator_category
<OutputIterator
, IteratorTag
>::value
,
873 operator()(Policy
&&, InputIterator
, OutputIterator
)
878 // These run_for_* structures specify with which types of iterators callable object Op
879 // should be executed.
880 template <typename Op
>
881 struct run_for_rnd
: non_const_wrapper_tagged
<Op
, std::random_access_iterator_tag
>
885 template <typename Op
>
886 struct run_for_rnd_bi
: non_const_wrapper_tagged
<Op
, std::forward_iterator_tag
, false>
890 template <typename Op
>
891 struct run_for_rnd_fw
: non_const_wrapper_tagged
<Op
, std::bidirectional_iterator_tag
, false>
895 // Invoker for different types of iterators.
896 template <typename IteratorTag
, typename IsReverse
>
897 struct iterator_invoker
899 template <typename Iterator
>
900 using make_iterator
= MakeIterator
<Iterator
, IteratorTag
, IsReverse
>;
901 template <typename Iterator
>
902 using IsConst
= typename
std::is_const
<
903 typename
std::remove_pointer
<typename
std::iterator_traits
<Iterator
>::pointer
>::type
>::type
;
904 template <typename Iterator
>
905 using invoke_if
= invoke_if_
<IsReverse
, IsConst
<Iterator
>>;
907 // A single iterator version which is used for non_const testcases
908 template <typename Policy
, typename Op
, typename Iterator
>
909 typename
std::enable_if
<is_same_iterator_category
<Iterator
, std::random_access_iterator_tag
>::value
&&
910 std::is_base_of
<non_const_wrapper
, Op
>::value
,
912 operator()(Policy
&& exec
, Op op
, Iterator iter
)
914 op(std::forward
<Policy
>(exec
), make_iterator
<Iterator
>()(iter
));
917 // A version with 2 iterators which is used for non_const testcases
918 template <typename Policy
, typename Op
, typename InputIterator
, typename OutputIterator
>
919 typename
std::enable_if
<is_same_iterator_category
<OutputIterator
, std::random_access_iterator_tag
>::value
&&
920 std::is_base_of
<non_const_wrapper
, Op
>::value
,
922 operator()(Policy
&& exec
, Op op
, InputIterator input_iter
, OutputIterator out_iter
)
924 op(std::forward
<Policy
>(exec
), make_iterator
<InputIterator
>()(input_iter
),
925 make_iterator
<OutputIterator
>()(out_iter
));
928 template <typename Policy
, typename Op
, typename Iterator
, typename Size
, typename
... Rest
>
929 typename
std::enable_if
<is_same_iterator_category
<Iterator
, std::random_access_iterator_tag
>::value
, void>::type
930 operator()(Policy
&& exec
, Op op
, Iterator begin
, Size n
, Rest
&&... rest
)
932 invoke_if
<Iterator
>()(n
<= sizeLimit
, op
, exec
, make_iterator
<Iterator
>()(begin
), n
,
933 std::forward
<Rest
>(rest
)...);
936 template <typename Policy
, typename Op
, typename Iterator
, typename
... Rest
>
937 typename
std::enable_if
<is_same_iterator_category
<Iterator
, std::random_access_iterator_tag
>::value
&&
938 !std::is_base_of
<non_const_wrapper
, Op
>::value
,
940 operator()(Policy
&& exec
, Op op
, Iterator inputBegin
, Iterator inputEnd
, Rest
&&... rest
)
942 invoke_if
<Iterator
>()(std::distance(inputBegin
, inputEnd
) <= sizeLimit
, op
, exec
,
943 make_iterator
<Iterator
>()(inputBegin
), make_iterator
<Iterator
>()(inputEnd
),
944 std::forward
<Rest
>(rest
)...);
947 template <typename Policy
, typename Op
, typename InputIterator
, typename OutputIterator
, typename
... Rest
>
948 typename
std::enable_if
<is_same_iterator_category
<OutputIterator
, std::random_access_iterator_tag
>::value
,
950 operator()(Policy
&& exec
, Op op
, InputIterator inputBegin
, InputIterator inputEnd
, OutputIterator outputBegin
,
953 invoke_if
<InputIterator
>()(std::distance(inputBegin
, inputEnd
) <= sizeLimit
, op
, exec
,
954 make_iterator
<InputIterator
>()(inputBegin
), make_iterator
<InputIterator
>()(inputEnd
),
955 make_iterator
<OutputIterator
>()(outputBegin
), std::forward
<Rest
>(rest
)...);
958 template <typename Policy
, typename Op
, typename InputIterator
, typename OutputIterator
, typename
... Rest
>
959 typename
std::enable_if
<is_same_iterator_category
<OutputIterator
, std::random_access_iterator_tag
>::value
,
961 operator()(Policy
&& exec
, Op op
, InputIterator inputBegin
, InputIterator inputEnd
, OutputIterator outputBegin
,
962 OutputIterator outputEnd
, Rest
&&... rest
)
964 invoke_if
<InputIterator
>()(std::distance(inputBegin
, inputEnd
) <= sizeLimit
, op
, exec
,
965 make_iterator
<InputIterator
>()(inputBegin
), make_iterator
<InputIterator
>()(inputEnd
),
966 make_iterator
<OutputIterator
>()(outputBegin
),
967 make_iterator
<OutputIterator
>()(outputEnd
), std::forward
<Rest
>(rest
)...);
970 template <typename Policy
, typename Op
, typename InputIterator1
, typename InputIterator2
, typename OutputIterator
,
972 typename
std::enable_if
<is_same_iterator_category
<OutputIterator
, std::random_access_iterator_tag
>::value
,
974 operator()(Policy
&& exec
, Op op
, InputIterator1 inputBegin1
, InputIterator1 inputEnd1
, InputIterator2 inputBegin2
,
975 InputIterator2 inputEnd2
, OutputIterator outputBegin
, OutputIterator outputEnd
, Rest
&&... rest
)
977 invoke_if
<InputIterator1
>()(
978 std::distance(inputBegin1
, inputEnd1
) <= sizeLimit
, op
, exec
, make_iterator
<InputIterator1
>()(inputBegin1
),
979 make_iterator
<InputIterator1
>()(inputEnd1
), make_iterator
<InputIterator2
>()(inputBegin2
),
980 make_iterator
<InputIterator2
>()(inputEnd2
), make_iterator
<OutputIterator
>()(outputBegin
),
981 make_iterator
<OutputIterator
>()(outputEnd
), std::forward
<Rest
>(rest
)...);
985 // Invoker for reverse iterators only
986 // Note: if we run with reverse iterators we shouldn't test the large range
987 template <typename IteratorTag
>
988 struct iterator_invoker
<IteratorTag
, /* IsReverse = */ std::true_type
>
991 template <typename Iterator
>
992 using make_iterator
= MakeIterator
<Iterator
, IteratorTag
, std::true_type
>;
994 // A single iterator version which is used for non_const testcases
995 template <typename Policy
, typename Op
, typename Iterator
>
996 typename
std::enable_if
<is_same_iterator_category
<Iterator
, std::random_access_iterator_tag
>::value
&&
997 std::is_base_of
<non_const_wrapper
, Op
>::value
,
999 operator()(Policy
&& exec
, Op op
, Iterator iter
)
1001 op(std::forward
<Policy
>(exec
), make_iterator
<Iterator
>()(iter
));
1004 // A version with 2 iterators which is used for non_const testcases
1005 template <typename Policy
, typename Op
, typename InputIterator
, typename OutputIterator
>
1006 typename
std::enable_if
<is_same_iterator_category
<OutputIterator
, std::random_access_iterator_tag
>::value
&&
1007 std::is_base_of
<non_const_wrapper
, Op
>::value
,
1009 operator()(Policy
&& exec
, Op op
, InputIterator input_iter
, OutputIterator out_iter
)
1011 op(std::forward
<Policy
>(exec
), make_iterator
<InputIterator
>()(input_iter
),
1012 make_iterator
<OutputIterator
>()(out_iter
));
1015 template <typename Policy
, typename Op
, typename Iterator
, typename Size
, typename
... Rest
>
1016 typename
std::enable_if
<is_same_iterator_category
<Iterator
, std::random_access_iterator_tag
>::value
, void>::type
1017 operator()(Policy
&& exec
, Op op
, Iterator begin
, Size n
, Rest
&&... rest
)
1020 op(exec
, make_iterator
<Iterator
>()(begin
+ n
), n
, std::forward
<Rest
>(rest
)...);
1023 template <typename Policy
, typename Op
, typename Iterator
, typename
... Rest
>
1024 typename
std::enable_if
<is_same_iterator_category
<Iterator
, std::random_access_iterator_tag
>::value
&&
1025 !std::is_base_of
<non_const_wrapper
, Op
>::value
,
1027 operator()(Policy
&& exec
, Op op
, Iterator inputBegin
, Iterator inputEnd
, Rest
&&... rest
)
1029 if (std::distance(inputBegin
, inputEnd
) <= sizeLimit
)
1030 op(exec
, make_iterator
<Iterator
>()(inputEnd
), make_iterator
<Iterator
>()(inputBegin
),
1031 std::forward
<Rest
>(rest
)...);
1034 template <typename Policy
, typename Op
, typename InputIterator
, typename OutputIterator
, typename
... Rest
>
1035 typename
std::enable_if
<is_same_iterator_category
<OutputIterator
, std::random_access_iterator_tag
>::value
,
1037 operator()(Policy
&& exec
, Op op
, InputIterator inputBegin
, InputIterator inputEnd
, OutputIterator outputBegin
,
1040 if (std::distance(inputBegin
, inputEnd
) <= sizeLimit
)
1041 op(exec
, make_iterator
<InputIterator
>()(inputEnd
), make_iterator
<InputIterator
>()(inputBegin
),
1042 make_iterator
<OutputIterator
>()(outputBegin
+ (inputEnd
- inputBegin
)), std::forward
<Rest
>(rest
)...);
1045 template <typename Policy
, typename Op
, typename InputIterator
, typename OutputIterator
, typename
... Rest
>
1046 typename
std::enable_if
<is_same_iterator_category
<OutputIterator
, std::random_access_iterator_tag
>::value
,
1048 operator()(Policy
&& exec
, Op op
, InputIterator inputBegin
, InputIterator inputEnd
, OutputIterator outputBegin
,
1049 OutputIterator outputEnd
, Rest
&&... rest
)
1051 if (std::distance(inputBegin
, inputEnd
) <= sizeLimit
)
1052 op(exec
, make_iterator
<InputIterator
>()(inputEnd
), make_iterator
<InputIterator
>()(inputBegin
),
1053 make_iterator
<OutputIterator
>()(outputEnd
), make_iterator
<OutputIterator
>()(outputBegin
),
1054 std::forward
<Rest
>(rest
)...);
1057 template <typename Policy
, typename Op
, typename InputIterator1
, typename InputIterator2
, typename OutputIterator
,
1059 typename
std::enable_if
<is_same_iterator_category
<OutputIterator
, std::random_access_iterator_tag
>::value
,
1061 operator()(Policy
&& exec
, Op op
, InputIterator1 inputBegin1
, InputIterator1 inputEnd1
, InputIterator2 inputBegin2
,
1062 InputIterator2 inputEnd2
, OutputIterator outputBegin
, OutputIterator outputEnd
, Rest
&&... rest
)
1064 if (std::distance(inputBegin1
, inputEnd1
) <= sizeLimit
)
1065 op(exec
, make_iterator
<InputIterator1
>()(inputEnd1
), make_iterator
<InputIterator1
>()(inputBegin1
),
1066 make_iterator
<InputIterator2
>()(inputEnd2
), make_iterator
<InputIterator2
>()(inputBegin2
),
1067 make_iterator
<OutputIterator
>()(outputEnd
), make_iterator
<OutputIterator
>()(outputBegin
),
1068 std::forward
<Rest
>(rest
)...);
1072 // We can't create reverse iterator from forward iterator
1074 struct iterator_invoker
<std::forward_iterator_tag
, /*isReverse=*/std::true_type
>
1076 template <typename
... Rest
>
1078 operator()(Rest
&&...)
1083 template <typename IsReverse
>
1084 struct reverse_invoker
1086 template <typename
... Rest
>
1088 operator()(Rest
&&... rest
)
1090 // Random-access iterator
1091 iterator_invoker
<std::random_access_iterator_tag
, IsReverse
>()(std::forward
<Rest
>(rest
)...);
1094 iterator_invoker
<std::forward_iterator_tag
, IsReverse
>()(std::forward
<Rest
>(rest
)...);
1096 // Bidirectional iterator
1097 iterator_invoker
<std::bidirectional_iterator_tag
, IsReverse
>()(std::forward
<Rest
>(rest
)...);
1101 struct invoke_on_all_iterator_types
1103 template <typename
... Rest
>
1105 operator()(Rest
&&... rest
)
1107 reverse_invoker
</* IsReverse = */ std::false_type
>()(std::forward
<Rest
>(rest
)...);
1108 reverse_invoker
</* IsReverse = */ std::true_type
>()(std::forward
<Rest
>(rest
)...);
1111 //============================================================================
1113 // Invoke op(policy,rest...) for each possible policy.
1114 template <typename Op
, typename
... T
>
1116 invoke_on_all_policies(Op op
, T
&&... rest
)
1118 using namespace __pstl::execution
;
1120 // Try static execution policies
1121 invoke_on_all_iterator_types()(seq
, op
, std::forward
<T
>(rest
)...);
1122 invoke_on_all_iterator_types()(unseq
, op
, std::forward
<T
>(rest
)...);
1123 invoke_on_all_iterator_types()(par
, op
, std::forward
<T
>(rest
)...);
1124 invoke_on_all_iterator_types()(par_unseq
, op
, std::forward
<T
>(rest
)...);
1127 template <typename F
>
1128 struct NonConstAdapter
1131 NonConstAdapter(const F
& f
) : my_f(f
) {}
1133 template <typename
... Types
>
1135 operator()(Types
&&... args
) -> decltype(std::declval
<F
>().
1136 operator()(std::forward
<Types
>(args
)...))
1138 return my_f(std::forward
<Types
>(args
)...);
1142 template <typename F
>
1144 non_const(const F
& f
)
1146 return NonConstAdapter
<F
>(f
);
1149 // Wrapper for types. It's need for counting of constructing and destructing objects
1150 template <typename T
>
1156 my_field
= std::shared_ptr
<T
>(new T());
1159 Wrapper(const T
& input
)
1161 my_field
= std::shared_ptr
<T
>(new T(input
));
1164 Wrapper(const Wrapper
& input
)
1166 my_field
= input
.my_field
;
1169 Wrapper(Wrapper
&& input
)
1171 my_field
= input
.my_field
;
1172 input
.my_field
= nullptr;
1176 operator=(const Wrapper
& input
)
1178 my_field
= input
.my_field
;
1182 operator=(Wrapper
&& input
)
1184 my_field
= input
.my_field
;
1185 input
.my_field
= nullptr;
1190 operator==(const Wrapper
& input
) const
1192 return my_field
== input
.my_field
;
1195 operator<(const Wrapper
& input
) const
1197 return *my_field
< *input
.my_field
;
1200 operator>(const Wrapper
& input
) const
1202 return *my_field
> *input
.my_field
;
1204 friend std::ostream
&
1205 operator<<(std::ostream
& stream
, const Wrapper
& input
)
1207 return stream
<< *(input
.my_field
);
1218 get_my_field() const
1220 return my_field
.get();
1233 SetCount(const size_t& n
)
1238 SetMoveCount(const size_t& n
)
1244 static std::atomic
<size_t> my_count
;
1245 static std::atomic
<size_t> move_count
;
1246 std::shared_ptr
<T
> my_field
;
1249 template <typename T
>
1250 std::atomic
<size_t> Wrapper
<T
>::my_count
= {0};
1252 template <typename T
>
1253 std::atomic
<size_t> Wrapper
<T
>::move_count
= {0};
1255 template <typename InputIterator
, typename T
, typename BinaryOperation
, typename UnaryOperation
>
1257 transform_reduce_serial(InputIterator first
, InputIterator last
, T init
, BinaryOperation binary_op
,
1258 UnaryOperation unary_op
) noexcept
1260 for (; first
!= last
; ++first
)
1262 init
= binary_op(init
, unary_op(*first
));
1270 #if defined(_PSTL_TEST_SUCCESSFUL_KEYWORD)
1277 // test_algo_basic_* functions are used to execute
1278 // f on a very basic sequence of elements of type T.
1280 // Should be used with unary predicate
1281 template <typename T
, typename F
>
1283 test_algo_basic_single(F
&& f
)
1286 Sequence
<T
> in(N
, [](size_t v
) -> T
{ return T(v
); });
1288 invoke_on_all_policies(f
, in
.begin());
1291 // Should be used with binary predicate
1292 template <typename T
, typename F
>
1294 test_algo_basic_double(F
&& f
)
1297 Sequence
<T
> in(N
, [](size_t v
) -> T
{ return T(v
); });
1298 Sequence
<T
> out(N
, [](size_t v
) -> T
{ return T(v
); });
1300 invoke_on_all_policies(f
, in
.begin(), out
.begin());
1303 template <typename Policy
, typename F
>
1305 invoke_if(Policy
&&, F f
)
1307 #if defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN)
1308 using decay_policy
= typename
std::decay
<Policy
>::type
;
1309 using allow_unsequenced
=
1310 std::integral_constant
<bool, (std::is_same
<decay_policy
, std::execution::unsequenced_policy
>::value
||
1311 std::is_same
<decay_policy
, std::execution::parallel_unsequenced_policy
>::value
)>;
1312 __pstl::__internal::__invoke_if_not(allow_unsequenced
{}, f
);
1318 } /* namespace TestUtils */