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