1 /*
2     Copyright (c) 2005-2021 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #define MAX_TUPLE_TEST_SIZE 10
18 #include "common/config.h"
19 
20 #include "test_join_node.h"
21 
22 #include "common/concepts_common.h"
23 
24 
25 //! \file test_join_node_key_matching.cpp
26 //! \brief Test for [flow_graph.join_node] specification
27 
28 
29 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
30 void test_deduction_guides() {
31     using namespace tbb::flow;
32     using tuple_type = std::tuple<int, int, double>;
33 
34     graph g;
35     auto body_int = [](const int&)->int { return 1; };
36     auto body_double = [](const double&)->int { return 1; };
37 
38     join_node j1(g, body_int, body_int, body_double);
39     static_assert(std::is_same_v<decltype(j1), join_node<tuple_type, key_matching<int>>>);
40 
41 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
42     broadcast_node<int> b1(g), b2(g);
43     broadcast_node<double> b3(g);
44     broadcast_node<tuple_type> b4(g);
45 
46     join_node j2(follows(b1, b2, b3), body_int, body_int, body_double);
47     static_assert(std::is_same_v<decltype(j2), join_node<tuple_type, key_matching<int>>>);
48 
49     join_node j3(precedes(b4), body_int, body_int, body_double);
50     static_assert(std::is_same_v<decltype(j3), join_node<tuple_type, key_matching<int>>>);
51 #endif
52 
53     join_node j4(j1);
54     static_assert(std::is_same_v<decltype(j4), join_node<tuple_type, key_matching<int>>>);
55 }
56 #endif
57 
58 template <typename T1, typename T2>
59 using make_tuple = decltype(std::tuple_cat(T1(), std::tuple<T2>()));
60 using T1 = std::tuple<MyKeyFirst<std::string, double>>;
61 using T2 = make_tuple<T1, MyKeySecond<std::string, int>>;
62 using T3 = make_tuple<T2, MyKeyFirst<std::string, int>>;
63 using T4 = make_tuple<T3, MyKeyWithBrokenMessageKey<std::string, size_t>>;
64 using T5 = make_tuple<T4, MyKeyWithBrokenMessageKey<std::string, int>>;
65 using T6 = make_tuple<T5, MyKeySecond<std::string, short>>;
66 using T7 = make_tuple<T6, MyKeySecond<std::string, threebyte>>;
67 using T8 = make_tuple<T7, MyKeyFirst<std::string, int>>;
68 using T9 = make_tuple<T8, MyKeySecond<std::string, threebyte>>;
69 using T10 = make_tuple<T9, MyKeyWithBrokenMessageKey<std::string, size_t>>;
70 
71 //! Test serial key matching on special input types
72 //! \brief \ref error_guessing
73 TEST_CASE("Serial test on tuples") {
74     INFO("key_matching\n");
75     generate_test<serial_test, std::tuple<MyKeyFirst<int, double>, MyKeySecond<int, float> >, tbb::flow::key_matching<int> >::do_test();
76     generate_test<serial_test, std::tuple<MyKeyFirst<std::string, double>, MyKeySecond<std::string, float> >, tbb::flow::key_matching<std::string> >::do_test();
77     generate_test<serial_test, std::tuple<MyKeyFirst<std::string, double>, MyKeySecond<std::string, float>, MyKeyWithBrokenMessageKey<std::string, int> >, tbb::flow::key_matching<std::string&> >::do_test();
78 }
79 
80 //! Serial test with different tuple sizes
81 //! \brief \ref error_guessing
82 TEST_CASE_TEMPLATE("Serial N tests on tuples", T, T2, T3, T4, T5, T6, T7, T8, T9, T10) {
83      generate_test<serial_test, T, tbb::flow::key_matching<std::string&>>::do_test();
84 }
85 
86 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
87 //! Test deduction guides
88 //! \brief \ref requirement
89 TEST_CASE("Test deduction guides"){
90     test_deduction_guides();
91 }
92 #endif
93 
94 //! Test parallel key matching on special input types
95 //! \brief \ref error_guessing
96 TEST_CASE("Parallel test on tuples"){
97     generate_test<parallel_test, std::tuple<MyKeyFirst<int, double>, MyKeySecond<int, float> >, tbb::flow::key_matching<int> >::do_test();
98     generate_test<parallel_test, std::tuple<MyKeyFirst<int, double>, MyKeySecond<int, float> >, tbb::flow::key_matching<int&> >::do_test();
99     generate_test<parallel_test, std::tuple<MyKeyFirst<std::string, double>, MyKeySecond<std::string, float> >, tbb::flow::key_matching<std::string&> >::do_test();
100 }
101 
102 //! Parallel test with different tuple sizes
103 //! \brief \ref error_guessing
104 TEST_CASE_TEMPLATE("Parallel N tests on tuples", T, T2, T3, T4, T5, T6, T7, T8, T9, T10) {
105     generate_test<parallel_test, T, tbb::flow::key_matching<std::string&>>::do_test();
106 }
107 
108 
109 #if __TBB_CPP20_CONCEPTS_PRESENT
110 template <std::size_t Count>
111 struct tuple_helper {
112     using type = decltype(std::tuple_cat(std::declval<std::tuple<int>>(), std::declval<typename tuple_helper<Count - 1>::type>()));
113 };
114 
115 template <>
116 struct tuple_helper<1> {
117     using type = std::tuple<int>;
118 };
119 
120 template <typename... Args>
121 concept can_initialize_join_node = requires(tbb::flow::graph& g, Args... args) {
122     tbb::flow::join_node<typename tuple_helper<sizeof...(Args)>::type,
123                          tbb::flow::key_matching<int>>(g, args...);
124 };
125 
126 // Helper for the concepts which checks if key_matching join_node cannot be instantiated if
127 // one of its constructor arguments do not satisfy join_node_function_object concept
128 // This structure substitutes IncorrectT to the sequence of arguments on IncorrectArgIndex position
129 // The remaining arguments in the sequence are CorrectT
130 template <std::size_t ArgCount, std::size_t IncorrectArgIndex, typename CorrectT, typename IncorrectT, typename... Args>
131 struct multiple_arguments_initialization_helper {
132     // Current index is not equal to IncorrectArgIndex - substitute CorrectT at the end of the arguments sequence and continue
133     static constexpr bool value = multiple_arguments_initialization_helper<ArgCount - 1, IncorrectArgIndex - 1, CorrectT, IncorrectT, Args..., CorrectT>::value;
134 };
135 
136 template <std::size_t ArgCount, typename CorrectT, typename IncorrectT, typename... Args>
137 struct multiple_arguments_initialization_helper<ArgCount, 0, CorrectT, IncorrectT, Args...> {
138     // Current index is equal to IncorrectArgIndex - substitute IncorrectT at the end of the sequence and continue
139     // No more incorrect indices would be added - continue with MAX_TUPLE_TEST_SIZE variable as current incorrect index
140     static constexpr bool value = multiple_arguments_initialization_helper<ArgCount - 1, MAX_TUPLE_TEST_SIZE, CorrectT, IncorrectT, Args..., IncorrectT>::value;
141 };
142 
143 template <std::size_t IncorrectArgIndex, typename CorrectT, typename IncorrectT, typename... Args>
144 struct multiple_arguments_initialization_helper<0, IncorrectArgIndex, CorrectT, IncorrectT, Args...> {
145     // ArgCount is equal to 0 - no more arguments should be added
146     // Check if join_node can be initialized with Args
147     static constexpr bool value = can_initialize_join_node<Args...>;
148 };
149 
150 // Helper which iterates over incorrect indices. value is true if initialization is successful for at least for one IncorrectArgIndex
151 template <std::size_t ArgCount, std::size_t CurrentIncorrectIndex, typename CorrectT, typename IncorrectT>
152 struct incorrect_arg_index_iteration_helper {
153     // CurrentIncorrectIndex is not equal to max - check with current and continue
154     static constexpr bool value = multiple_arguments_initialization_helper<ArgCount, CurrentIncorrectIndex, CorrectT, IncorrectT>::value ||
155                                   incorrect_arg_index_iteration_helper<ArgCount, CurrentIncorrectIndex + 1, CorrectT, IncorrectT>::value;
156 };
157 
158 template <std::size_t ArgCount, std::size_t CurrentIncorrectIndex, typename CorrectT, typename IncorrectT>
159 requires (ArgCount == CurrentIncorrectIndex + 1)
160 struct incorrect_arg_index_iteration_helper<ArgCount, CurrentIncorrectIndex, CorrectT, IncorrectT> {
161     // CurrentIncorrectIndex is equal to max - check and stop
162     static constexpr bool value = multiple_arguments_initialization_helper<ArgCount, CurrentIncorrectIndex, CorrectT, IncorrectT>::value;
163 };
164 
165 // Helper which iterates over argument count. value is true if initialization (with all possible incorrect indices) is successful for at least one ArgCount
166 template <std::size_t CurrentArgCount, typename CorrectT, typename IncorrectT>
167 struct arg_count_iteration_helper {
168     // CurrentArgCount is not equal to max - check and continue
169     static constexpr bool value = incorrect_arg_index_iteration_helper<CurrentArgCount, /*StartIncorrectIndex = */0, CorrectT, IncorrectT>::value ||
170                                   arg_count_iteration_helper<CurrentArgCount + 1, CorrectT, IncorrectT>::value;
171 };
172 
173 template <typename CorrectT, typename IncorrectT>
174 struct arg_count_iteration_helper<MAX_TUPLE_TEST_SIZE, CorrectT, IncorrectT> {
175     // CurrentArgCount is equal to max - check and stop
176     static constexpr bool value = incorrect_arg_index_iteration_helper<MAX_TUPLE_TEST_SIZE, /*StartIncorrectIndex = */0, CorrectT, IncorrectT>::value;
177 };
178 
179 template <typename CorrectT, typename IncorrectT>
180 concept can_initialize_join_node_with_incorrect_argument = arg_count_iteration_helper</*StartArgCount = */2, CorrectT, IncorrectT>::value;
181 
182 template <std::size_t CurrentArgCount, typename CorrectT, typename... Args>
183 struct join_node_correct_initialization_helper {
184     static constexpr bool value = join_node_correct_initialization_helper<CurrentArgCount - 1, CorrectT, Args..., CorrectT>::value;
185 };
186 
187 template <typename CorrectT, typename... Args>
188 struct join_node_correct_initialization_helper<0, CorrectT, Args...> {
189     static constexpr bool value = can_initialize_join_node<Args...>;
190 };
191 
192 template <std::size_t CurrentArgCount, typename CorrectT>
193 struct arg_count_correct_initialization_helper {
194     static constexpr bool value = join_node_correct_initialization_helper<CurrentArgCount, CorrectT>::value &&
195                                   arg_count_correct_initialization_helper<CurrentArgCount + 1, CorrectT>::value;
196 };
197 
198 template <typename CorrectT>
199 struct arg_count_correct_initialization_helper<MAX_TUPLE_TEST_SIZE, CorrectT> {
200     static constexpr bool value = join_node_correct_initialization_helper<MAX_TUPLE_TEST_SIZE, CorrectT>::value;
201 };
202 
203 template <typename CorrectT>
204 concept can_initialize_join_node_with_correct_argument = arg_count_correct_initialization_helper</*Start = */2, CorrectT>::value;
205 
206 //! \brief \ref error_guessing
207 TEST_CASE("join_node constraints") {
208     using namespace test_concepts::join_node_function_object;
209     static_assert(can_initialize_join_node_with_correct_argument<Correct<int, int>>);
210     static_assert(!can_initialize_join_node_with_incorrect_argument<Correct<int, int>, NonCopyable<int, int>>);
211     static_assert(!can_initialize_join_node_with_incorrect_argument<Correct<int, int>, NonDestructible<int, int>>);
212     static_assert(!can_initialize_join_node_with_incorrect_argument<Correct<int, int>, NoOperatorRoundBrackets<int, int>>);
213     static_assert(!can_initialize_join_node_with_incorrect_argument<Correct<int, int>, WrongInputOperatorRoundBrackets<int, int>>);
214     static_assert(!can_initialize_join_node_with_incorrect_argument<Correct<int, int>, WrongReturnOperatorRoundBrackets<int, int>>);
215 }
216 #endif // __TBB_CPP20_CONCEPTS_PRESENT
217