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