151c0b2f7Stbbdev /*
2de0a3a65SKonstantin Boyarinov Copyright (c) 2005-2023 Intel Corporation
351c0b2f7Stbbdev
451c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev you may not use this file except in compliance with the License.
651c0b2f7Stbbdev You may obtain a copy of the License at
751c0b2f7Stbbdev
851c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev
1051c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev See the License for the specific language governing permissions and
1451c0b2f7Stbbdev limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev
1751c0b2f7Stbbdev #include "common/parallel_for_each_common.h"
18478de5b1Stbbdev #include "common/concepts_common.h"
19478de5b1Stbbdev #include <vector>
20478de5b1Stbbdev #include <iterator>
2151c0b2f7Stbbdev
2251c0b2f7Stbbdev //! \file test_parallel_for_each.cpp
2351c0b2f7Stbbdev //! \brief Test for [algorithms.parallel_for_each]
2451c0b2f7Stbbdev
25de0a3a65SKonstantin Boyarinov #if __TBB_CPP20_PRESENT
26de0a3a65SKonstantin Boyarinov // Fancy iterator type that models the C++20 iterator type
27de0a3a65SKonstantin Boyarinov // that defines the real iterator category using iterator_concept type
28de0a3a65SKonstantin Boyarinov // and iterator_category is always std::input_iterator_type
29de0a3a65SKonstantin Boyarinov // Similar iterators are used by C++20 ranges (e.g. std::ranges::iota_view::iterator)
30de0a3a65SKonstantin Boyarinov // parallel_for_each algorithm should detect such iterators with respect to iterator_concept value
31de0a3a65SKonstantin Boyarinov
32de0a3a65SKonstantin Boyarinov template <typename T, typename Category>
33de0a3a65SKonstantin Boyarinov struct cpp20_iterator {
34de0a3a65SKonstantin Boyarinov static_assert(std::derived_from<Category, std::forward_iterator_tag>,
35de0a3a65SKonstantin Boyarinov "cpp20_iterator should be of at least forward iterator category");
36de0a3a65SKonstantin Boyarinov
37de0a3a65SKonstantin Boyarinov using iterator_concept = Category;
38de0a3a65SKonstantin Boyarinov using iterator_category = std::input_iterator_tag;
39de0a3a65SKonstantin Boyarinov using value_type = T;
40de0a3a65SKonstantin Boyarinov using difference_type = std::ptrdiff_t;
41de0a3a65SKonstantin Boyarinov
42de0a3a65SKonstantin Boyarinov cpp20_iterator() = default;
cpp20_iteratorcpp20_iterator43de0a3a65SKonstantin Boyarinov explicit cpp20_iterator(T* ptr) : my_ptr(ptr) {}
44de0a3a65SKonstantin Boyarinov
operator *cpp20_iterator45de0a3a65SKonstantin Boyarinov T& operator*() const { return *my_ptr; }
46de0a3a65SKonstantin Boyarinov
operator ++cpp20_iterator47de0a3a65SKonstantin Boyarinov cpp20_iterator& operator++() {
48de0a3a65SKonstantin Boyarinov ++my_ptr;
49de0a3a65SKonstantin Boyarinov return *this;
50de0a3a65SKonstantin Boyarinov }
51de0a3a65SKonstantin Boyarinov
operator ++cpp20_iterator52de0a3a65SKonstantin Boyarinov cpp20_iterator operator++(int) {
53de0a3a65SKonstantin Boyarinov auto it = *this;
54de0a3a65SKonstantin Boyarinov ++*this;
55de0a3a65SKonstantin Boyarinov return it;
56de0a3a65SKonstantin Boyarinov }
57de0a3a65SKonstantin Boyarinov
operator --cpp20_iterator58de0a3a65SKonstantin Boyarinov cpp20_iterator& operator--()
59de0a3a65SKonstantin Boyarinov requires std::derived_from<Category, std::bidirectional_iterator_tag>
60de0a3a65SKonstantin Boyarinov {
61de0a3a65SKonstantin Boyarinov --my_ptr;
62de0a3a65SKonstantin Boyarinov return *this;
63de0a3a65SKonstantin Boyarinov }
64de0a3a65SKonstantin Boyarinov
operator --cpp20_iterator65de0a3a65SKonstantin Boyarinov cpp20_iterator operator--(int)
66de0a3a65SKonstantin Boyarinov requires std::derived_from<Category, std::bidirectional_iterator_tag>
67de0a3a65SKonstantin Boyarinov {
68de0a3a65SKonstantin Boyarinov auto it = *this;
69de0a3a65SKonstantin Boyarinov --*this;
70de0a3a65SKonstantin Boyarinov return it;
71de0a3a65SKonstantin Boyarinov }
72de0a3a65SKonstantin Boyarinov
operator +=cpp20_iterator73de0a3a65SKonstantin Boyarinov cpp20_iterator& operator+=(difference_type n)
74de0a3a65SKonstantin Boyarinov requires std::derived_from<Category, std::random_access_iterator_tag>
75de0a3a65SKonstantin Boyarinov {
76de0a3a65SKonstantin Boyarinov my_ptr += n;
77de0a3a65SKonstantin Boyarinov return *this;
78de0a3a65SKonstantin Boyarinov }
79de0a3a65SKonstantin Boyarinov
operator -=cpp20_iterator80de0a3a65SKonstantin Boyarinov cpp20_iterator& operator-=(difference_type n)
81de0a3a65SKonstantin Boyarinov requires std::derived_from<Category, std::random_access_iterator_tag>
82de0a3a65SKonstantin Boyarinov {
83de0a3a65SKonstantin Boyarinov my_ptr -= n;
84de0a3a65SKonstantin Boyarinov return *this;
85de0a3a65SKonstantin Boyarinov }
86de0a3a65SKonstantin Boyarinov
operator []cpp20_iterator87de0a3a65SKonstantin Boyarinov T& operator[](difference_type n) const
88de0a3a65SKonstantin Boyarinov requires std::derived_from<Category, std::random_access_iterator_tag>
89de0a3a65SKonstantin Boyarinov {
90de0a3a65SKonstantin Boyarinov return my_ptr[n];
91de0a3a65SKonstantin Boyarinov }
92de0a3a65SKonstantin Boyarinov
93de0a3a65SKonstantin Boyarinov friend bool operator==(const cpp20_iterator&, const cpp20_iterator&) = default;
94de0a3a65SKonstantin Boyarinov
95de0a3a65SKonstantin Boyarinov friend auto operator<=>(const cpp20_iterator&, const cpp20_iterator&)
96de0a3a65SKonstantin Boyarinov requires std::derived_from<Category, std::random_access_iterator_tag> = default;
97de0a3a65SKonstantin Boyarinov
operator +(cpp20_iterator i,difference_type n)98de0a3a65SKonstantin Boyarinov friend cpp20_iterator operator+(cpp20_iterator i, difference_type n)
99de0a3a65SKonstantin Boyarinov requires std::derived_from<Category, std::random_access_iterator_tag>
100de0a3a65SKonstantin Boyarinov {
101de0a3a65SKonstantin Boyarinov return cpp20_iterator(i.my_ptr + n);
102de0a3a65SKonstantin Boyarinov }
103de0a3a65SKonstantin Boyarinov
operator +(difference_type n,cpp20_iterator i)104de0a3a65SKonstantin Boyarinov friend cpp20_iterator operator+(difference_type n, cpp20_iterator i)
105de0a3a65SKonstantin Boyarinov requires std::derived_from<Category, std::random_access_iterator_tag>
106de0a3a65SKonstantin Boyarinov {
107de0a3a65SKonstantin Boyarinov return i + n;
108de0a3a65SKonstantin Boyarinov }
109de0a3a65SKonstantin Boyarinov
operator -(cpp20_iterator i,difference_type n)110de0a3a65SKonstantin Boyarinov friend cpp20_iterator operator-(cpp20_iterator i, difference_type n)
111de0a3a65SKonstantin Boyarinov requires std::derived_from<Category, std::random_access_iterator_tag>
112de0a3a65SKonstantin Boyarinov {
113de0a3a65SKonstantin Boyarinov return cpp20_iterator(i.my_ptr - n);
114de0a3a65SKonstantin Boyarinov }
115de0a3a65SKonstantin Boyarinov
operator -(const cpp20_iterator & x,const cpp20_iterator & y)116de0a3a65SKonstantin Boyarinov friend difference_type operator-(const cpp20_iterator& x, const cpp20_iterator& y) {
117de0a3a65SKonstantin Boyarinov return x.my_ptr - y.my_ptr;
118de0a3a65SKonstantin Boyarinov }
119de0a3a65SKonstantin Boyarinov private:
120de0a3a65SKonstantin Boyarinov T* my_ptr = nullptr;
121de0a3a65SKonstantin Boyarinov }; // class cpp20_iterator
122de0a3a65SKonstantin Boyarinov #endif // __TBB_CPP20_PRESENT
123de0a3a65SKonstantin Boyarinov
12451c0b2f7Stbbdev //! Test forward access iterator support
12551c0b2f7Stbbdev //! \brief \ref error_guessing \ref interface
12651c0b2f7Stbbdev TEST_CASE("Forward iterator support") {
12751c0b2f7Stbbdev for ( auto concurrency_level : utils::concurrency_range() ) {
12851c0b2f7Stbbdev tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
12951c0b2f7Stbbdev for(size_t depth = 0; depth <= depths_nubmer; ++depth) {
13051c0b2f7Stbbdev g_tasks_expected = 0;
13151c0b2f7Stbbdev for (size_t i=0; i < depth; ++i)
13251c0b2f7Stbbdev g_tasks_expected += FindNumOfTasks(g_depths[i].value());
13351c0b2f7Stbbdev TestIterator_Modifiable<utils::ForwardIterator<value_t>>(depth);
13451c0b2f7Stbbdev }
13551c0b2f7Stbbdev }
13651c0b2f7Stbbdev }
13751c0b2f7Stbbdev
13851c0b2f7Stbbdev //! Test random access iterator support
13951c0b2f7Stbbdev //! \brief \ref error_guessing \ref interface
14051c0b2f7Stbbdev TEST_CASE("Random access iterator support") {
14151c0b2f7Stbbdev for ( auto concurrency_level : utils::concurrency_range() ) {
14251c0b2f7Stbbdev tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
14351c0b2f7Stbbdev for(size_t depth = 0; depth <= depths_nubmer; ++depth) {
14451c0b2f7Stbbdev g_tasks_expected = 0;
14551c0b2f7Stbbdev for (size_t i=0; i < depth; ++i)
14651c0b2f7Stbbdev g_tasks_expected += FindNumOfTasks(g_depths[i].value());
14751c0b2f7Stbbdev TestIterator_Modifiable<value_t*>(depth);
14851c0b2f7Stbbdev }
14951c0b2f7Stbbdev }
15051c0b2f7Stbbdev }
15151c0b2f7Stbbdev
15251c0b2f7Stbbdev //! Test const random access iterator support
15351c0b2f7Stbbdev //! \brief \ref error_guessing \ref interface
15451c0b2f7Stbbdev TEST_CASE("Const random access iterator support") {
15551c0b2f7Stbbdev for ( auto concurrency_level : utils::concurrency_range() ) {
15651c0b2f7Stbbdev tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
15751c0b2f7Stbbdev for(size_t depth = 0; depth <= depths_nubmer; ++depth) {
15851c0b2f7Stbbdev g_tasks_expected = 0;
15951c0b2f7Stbbdev for (size_t i=0; i < depth; ++i)
16051c0b2f7Stbbdev g_tasks_expected += FindNumOfTasks(g_depths[i].value());
16151c0b2f7Stbbdev TestIterator_Const<utils::ConstRandomIterator<value_t>>(depth);
16251c0b2f7Stbbdev }
16351c0b2f7Stbbdev }
16451c0b2f7Stbbdev
16551c0b2f7Stbbdev }
16651c0b2f7Stbbdev
16751c0b2f7Stbbdev //! Test container based overload
16851c0b2f7Stbbdev //! \brief \ref error_guessing \ref interface
16951c0b2f7Stbbdev TEST_CASE("Container based overload - forward iterator based container") {
17051c0b2f7Stbbdev container_based_overload_test_case<utils::ForwardIterator>(/*expected_value*/1);
17151c0b2f7Stbbdev }
17251c0b2f7Stbbdev
17351c0b2f7Stbbdev //! Test container based overload
17451c0b2f7Stbbdev //! \brief \ref error_guessing \ref interface
17551c0b2f7Stbbdev TEST_CASE("Container based overload - random access iterator based container") {
17651c0b2f7Stbbdev container_based_overload_test_case<utils::RandomIterator>(/*expected_value*/1);
17751c0b2f7Stbbdev }
17851c0b2f7Stbbdev
17951c0b2f7Stbbdev // Test for iterators over values convertible to work item type
18051c0b2f7Stbbdev //! \brief \ref error_guessing \ref interface
18151c0b2f7Stbbdev TEST_CASE("Using with values convertible to work item type") {
18251c0b2f7Stbbdev for ( auto concurrency_level : utils::concurrency_range() ) {
18351c0b2f7Stbbdev tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
18451c0b2f7Stbbdev using Iterator = size_t*;
18551c0b2f7Stbbdev for(size_t depth = 0; depth <= depths_nubmer; ++depth) {
18651c0b2f7Stbbdev g_tasks_expected = 0;
18751c0b2f7Stbbdev for (size_t i=0; i < depth; ++i)
18851c0b2f7Stbbdev g_tasks_expected += FindNumOfTasks(g_depths[i].value());
18951c0b2f7Stbbdev // Test for iterators over values convertible to work item type
19051c0b2f7Stbbdev TestIterator_Common<Iterator>(depth);
19151c0b2f7Stbbdev TestBody<FakeTaskGeneratorBody_RvalueRefVersion, Iterator>(depth);
19251c0b2f7Stbbdev TestBody<TaskGeneratorBody_RvalueRefVersion, Iterator>(depth);
19351c0b2f7Stbbdev }
19451c0b2f7Stbbdev }
19551c0b2f7Stbbdev }
19651c0b2f7Stbbdev
19751c0b2f7Stbbdev //! Testing workers going to sleep
19851c0b2f7Stbbdev //! \brief \ref resource_usage \ref stress
19951c0b2f7Stbbdev TEST_CASE("That all workers sleep when no work") {
20051c0b2f7Stbbdev const std::size_t N = 100000;
20151c0b2f7Stbbdev std::vector<std::size_t> vec(N, 0);
20251c0b2f7Stbbdev
__anon577ef66d0102(std::size_t& in) 20351c0b2f7Stbbdev tbb::parallel_for_each(vec.begin(), vec.end(), [&](std::size_t& in) {
204b15aabb3Stbbdev for (int i = 0; i < 1000; ++i) {
20551c0b2f7Stbbdev ++in;
20651c0b2f7Stbbdev }
20751c0b2f7Stbbdev });
20851c0b2f7Stbbdev TestCPUUserTime(utils::get_platform_max_threads());
20951c0b2f7Stbbdev }
210478de5b1Stbbdev
211478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
212478de5b1Stbbdev
213478de5b1Stbbdev template <typename Iterator, typename Body>
214478de5b1Stbbdev concept can_call_parallel_for_each_with_iterator = requires( Iterator it, const Body& body, tbb::task_group_context ctx ) {
215478de5b1Stbbdev tbb::parallel_for_each(it, it, body);
216478de5b1Stbbdev tbb::parallel_for_each(it, it, body, ctx);
217478de5b1Stbbdev };
218478de5b1Stbbdev
219478de5b1Stbbdev template <typename ContainerBasedSequence, typename Body>
220478de5b1Stbbdev concept can_call_parallel_for_each_with_cbs = requires( ContainerBasedSequence cbs,
221478de5b1Stbbdev const ContainerBasedSequence const_cbs,
222478de5b1Stbbdev const Body& body, tbb::task_group_context ctx ) {
223478de5b1Stbbdev tbb::parallel_for_each(cbs, body);
224478de5b1Stbbdev tbb::parallel_for_each(cbs, body, ctx);
225478de5b1Stbbdev tbb::parallel_for_each(const_cbs, body);
226478de5b1Stbbdev tbb::parallel_for_each(const_cbs, body, ctx);
227478de5b1Stbbdev };
228478de5b1Stbbdev
2294eec89feSIvan Kochin using CorrectCBS = test_concepts::container_based_sequence::Correct;
2304eec89feSIvan Kochin
231478de5b1Stbbdev template <typename Body>
232478de5b1Stbbdev concept can_call_parallel_for_each =
2334eec89feSIvan Kochin can_call_parallel_for_each_with_iterator<CorrectCBS::iterator, Body> &&
2344eec89feSIvan Kochin can_call_parallel_for_each_with_cbs<CorrectCBS, Body>;
235478de5b1Stbbdev
236478de5b1Stbbdev template <typename Iterator>
237478de5b1Stbbdev using CorrectBody = test_concepts::parallel_for_each_body::Correct<decltype(*std::declval<Iterator>())>;
238478de5b1Stbbdev
test_pfor_each_iterator_constraints()239478de5b1Stbbdev void test_pfor_each_iterator_constraints() {
240478de5b1Stbbdev using CorrectIterator = typename std::vector<int>::iterator; // random_access_iterator
241478de5b1Stbbdev using IncorrectIterator = std::ostream_iterator<int>; // output_iterator
242478de5b1Stbbdev static_assert(can_call_parallel_for_each_with_iterator<CorrectIterator, CorrectBody<CorrectIterator>>);
243478de5b1Stbbdev static_assert(!can_call_parallel_for_each_with_iterator<IncorrectIterator, CorrectBody<IncorrectIterator>>);
244478de5b1Stbbdev }
245478de5b1Stbbdev
test_pfor_each_container_based_sequence_constraints()246478de5b1Stbbdev void test_pfor_each_container_based_sequence_constraints() {
247478de5b1Stbbdev using namespace test_concepts::container_based_sequence;
2484eec89feSIvan Kochin static_assert(can_call_parallel_for_each_with_cbs<Correct, CorrectBody<Correct::iterator>>);
2494eec89feSIvan Kochin static_assert(!can_call_parallel_for_each_with_cbs<NoBegin, CorrectBody<NoBegin::iterator>>);
2504eec89feSIvan Kochin static_assert(!can_call_parallel_for_each_with_cbs<NoEnd, CorrectBody<NoEnd::iterator>>);
251478de5b1Stbbdev }
252478de5b1Stbbdev
test_pfor_each_body_constraints()253478de5b1Stbbdev void test_pfor_each_body_constraints() {
254478de5b1Stbbdev using namespace test_concepts::parallel_for_each_body;
255478de5b1Stbbdev static_assert(can_call_parallel_for_each<Correct<int>>);
256478de5b1Stbbdev static_assert(can_call_parallel_for_each<WithFeeder<int>>);
257478de5b1Stbbdev static_assert(!can_call_parallel_for_each<NoOperatorRoundBrackets<int>>);
258478de5b1Stbbdev static_assert(!can_call_parallel_for_each<WithFeederNoOperatorRoundBrackets<int>>);
259478de5b1Stbbdev static_assert(!can_call_parallel_for_each<OperatorRoundBracketsNonConst<int>>);
260478de5b1Stbbdev static_assert(!can_call_parallel_for_each<WithFeederOperatorRoundBracketsNonConst<int>>);
261478de5b1Stbbdev static_assert(!can_call_parallel_for_each<WrongInputOperatorRoundBrackets<int>>);
262478de5b1Stbbdev static_assert(!can_call_parallel_for_each<WithFeederWrongFirstInputOperatorRoundBrackets<int>>);
263478de5b1Stbbdev static_assert(!can_call_parallel_for_each<WithFeederWrongSecondInputOperatorRoundBrackets<int>>);
264478de5b1Stbbdev }
265478de5b1Stbbdev
266478de5b1Stbbdev //! \brief \ref error_guessing
267478de5b1Stbbdev TEST_CASE("parallel_for_each constraints") {
268478de5b1Stbbdev test_pfor_each_iterator_constraints();
269478de5b1Stbbdev test_pfor_each_container_based_sequence_constraints();
270478de5b1Stbbdev test_pfor_each_body_constraints();
271478de5b1Stbbdev }
272478de5b1Stbbdev
273478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
274de0a3a65SKonstantin Boyarinov
275de0a3a65SKonstantin Boyarinov #if __TBB_CPP20_PRESENT
276de0a3a65SKonstantin Boyarinov
277de0a3a65SKonstantin Boyarinov struct no_copy_move {
278de0a3a65SKonstantin Boyarinov no_copy_move() = default;
279de0a3a65SKonstantin Boyarinov
280de0a3a65SKonstantin Boyarinov no_copy_move(const no_copy_move&) = delete;
281de0a3a65SKonstantin Boyarinov no_copy_move(no_copy_move&&) = delete;
282de0a3a65SKonstantin Boyarinov
283de0a3a65SKonstantin Boyarinov no_copy_move& operator=(const no_copy_move&) = delete;
284de0a3a65SKonstantin Boyarinov no_copy_move& operator=(no_copy_move&&) = delete;
285de0a3a65SKonstantin Boyarinov
286de0a3a65SKonstantin Boyarinov int item = 0;
287de0a3a65SKonstantin Boyarinov };
288de0a3a65SKonstantin Boyarinov
289de0a3a65SKonstantin Boyarinov template <typename Category>
test_with_cpp20_iterator()290de0a3a65SKonstantin Boyarinov void test_with_cpp20_iterator() {
291de0a3a65SKonstantin Boyarinov constexpr std::size_t n = 1'000'000;
292de0a3a65SKonstantin Boyarinov
293*3b9f9baeSKonstantin Boyarinov std::vector<no_copy_move> elements(n);
294de0a3a65SKonstantin Boyarinov
295*3b9f9baeSKonstantin Boyarinov cpp20_iterator<no_copy_move, Category> begin(elements.data());
296*3b9f9baeSKonstantin Boyarinov cpp20_iterator<no_copy_move, Category> end(elements.data() + n);
297de0a3a65SKonstantin Boyarinov
298de0a3a65SKonstantin Boyarinov oneapi::tbb::parallel_for_each(begin, end, [](no_copy_move& element) {
299de0a3a65SKonstantin Boyarinov element.item = 42;
300de0a3a65SKonstantin Boyarinov });
301de0a3a65SKonstantin Boyarinov
302de0a3a65SKonstantin Boyarinov for (std::size_t index = 0; index < n; ++index) {
303de0a3a65SKonstantin Boyarinov CHECK(elements[index].item == 42);
304de0a3a65SKonstantin Boyarinov }
305de0a3a65SKonstantin Boyarinov }
306de0a3a65SKonstantin Boyarinov
307de0a3a65SKonstantin Boyarinov //! \brief \ref error_guessing \ref regression
308de0a3a65SKonstantin Boyarinov TEST_CASE("parallel_for_each with cpp20 iterator") {
309de0a3a65SKonstantin Boyarinov // Test that parallel_for_each threats ignores iterator_category type
310de0a3a65SKonstantin Boyarinov // if iterator_concept type is defined for iterator
311de0a3a65SKonstantin Boyarinov
312de0a3a65SKonstantin Boyarinov // For input iterators parallel_for_each requires element to be
313de0a3a65SKonstantin Boyarinov // copyable or movable so since cpp20_iterator is at least forward
314de0a3a65SKonstantin Boyarinov // parallel_for_each should work with cpp20_iterator
315de0a3a65SKonstantin Boyarinov // on non-copyable and non-movable type
316de0a3a65SKonstantin Boyarinov
317de0a3a65SKonstantin Boyarinov // test cpp20_iterator implementation
318de0a3a65SKonstantin Boyarinov using cpp20_forward_iterator = cpp20_iterator<int, std::forward_iterator_tag>;
319de0a3a65SKonstantin Boyarinov using cpp20_bidirectional_iterator = cpp20_iterator<int, std::bidirectional_iterator_tag>;
320de0a3a65SKonstantin Boyarinov using cpp20_random_access_iterator = cpp20_iterator<int, std::random_access_iterator_tag>;
321de0a3a65SKonstantin Boyarinov
322de0a3a65SKonstantin Boyarinov static_assert(std::forward_iterator<cpp20_forward_iterator>);
323de0a3a65SKonstantin Boyarinov static_assert(!std::bidirectional_iterator<cpp20_forward_iterator>);
324de0a3a65SKonstantin Boyarinov
325de0a3a65SKonstantin Boyarinov static_assert(std::bidirectional_iterator<cpp20_bidirectional_iterator>);
326de0a3a65SKonstantin Boyarinov static_assert(!std::random_access_iterator<cpp20_bidirectional_iterator>);
327de0a3a65SKonstantin Boyarinov
328de0a3a65SKonstantin Boyarinov static_assert(std::random_access_iterator<cpp20_random_access_iterator>);
329de0a3a65SKonstantin Boyarinov
330de0a3a65SKonstantin Boyarinov test_with_cpp20_iterator<std::forward_iterator_tag>();
331de0a3a65SKonstantin Boyarinov test_with_cpp20_iterator<std::bidirectional_iterator_tag>();
332de0a3a65SKonstantin Boyarinov test_with_cpp20_iterator<std::random_access_iterator_tag>();
333de0a3a65SKonstantin Boyarinov }
334de0a3a65SKonstantin Boyarinov
335de0a3a65SKonstantin Boyarinov #endif // __TBB_CPP20_PRESENT
336