151c0b2f7Stbbdev /*
2*a088cfa0SKonstantin Boyarinov Copyright (c) 2020-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
17b15aabb3Stbbdev #if __INTEL_COMPILER && _MSC_VER
18b15aabb3Stbbdev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
19b15aabb3Stbbdev #endif
2051c0b2f7Stbbdev
2151c0b2f7Stbbdev #include "conformance_flowgraph.h"
22*a088cfa0SKonstantin Boyarinov #include "common/test_invoke.h"
2351c0b2f7Stbbdev
2451c0b2f7Stbbdev //! \file conformance_join_node.cpp
2551c0b2f7Stbbdev //! \brief Test for [flow_graph.join_node] specification
2651c0b2f7Stbbdev
27de0109beSIlya Mishin using input_msg = conformance::message</*default_ctor*/true, /*copy_ctor*/true, /*copy_assign*/true>;
28de0109beSIlya Mishin using my_input_tuple = std::tuple<int, float, input_msg>;
2951c0b2f7Stbbdev
get_values(conformance::test_push_receiver<my_input_tuple> & rr)30de0109beSIlya Mishin std::vector<my_input_tuple> get_values( conformance::test_push_receiver<my_input_tuple>& rr ) {
31de0109beSIlya Mishin std::vector<my_input_tuple> messages;
32274f68e5SIlya Isaev my_input_tuple tmp(0, 0.f, input_msg(0));
33274f68e5SIlya Isaev while(rr.try_get(tmp)) {
34de0109beSIlya Mishin messages.push_back(tmp);
35de0109beSIlya Mishin }
36de0109beSIlya Mishin return messages;
3751c0b2f7Stbbdev }
3851c0b2f7Stbbdev
39de0109beSIlya Mishin #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
test_deduction_guides()40de0109beSIlya Mishin void test_deduction_guides() {
41de0109beSIlya Mishin using namespace tbb::flow;
4251c0b2f7Stbbdev
4351c0b2f7Stbbdev graph g;
44de0109beSIlya Mishin using tuple_type = std::tuple<int, int, int>;
45de0109beSIlya Mishin broadcast_node<int> b1(g), b2(g), b3(g);
46de0109beSIlya Mishin broadcast_node<tuple_type> b4(g);
47de0109beSIlya Mishin join_node<tuple_type> j0(g);
4851c0b2f7Stbbdev
49de0109beSIlya Mishin #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
50de0109beSIlya Mishin join_node j1(follows(b1, b2, b3));
51de0109beSIlya Mishin static_assert(std::is_same_v<decltype(j1), join_node<tuple_type>>);
52de0109beSIlya Mishin
53de0109beSIlya Mishin join_node j2(follows(b1, b2, b3), reserving());
54de0109beSIlya Mishin static_assert(std::is_same_v<decltype(j2), join_node<tuple_type, reserving>>);
55de0109beSIlya Mishin
56de0109beSIlya Mishin join_node j3(precedes(b4));
57de0109beSIlya Mishin static_assert(std::is_same_v<decltype(j3), join_node<tuple_type>>);
58de0109beSIlya Mishin
59de0109beSIlya Mishin join_node j4(precedes(b4), reserving());
60de0109beSIlya Mishin static_assert(std::is_same_v<decltype(j4), join_node<tuple_type, reserving>>);
61de0109beSIlya Mishin #endif
62de0109beSIlya Mishin
63de0109beSIlya Mishin join_node j5(j0);
64de0109beSIlya Mishin static_assert(std::is_same_v<decltype(j5), join_node<tuple_type>>);
6551c0b2f7Stbbdev }
6651c0b2f7Stbbdev
67de0109beSIlya Mishin #endif
6851c0b2f7Stbbdev
69de0109beSIlya Mishin //! The node that is constructed has a reference to the same graph object as src.
70de0109beSIlya Mishin //! The list of predecessors, messages in the input ports, and successors are not copied.
7151c0b2f7Stbbdev //! \brief \ref interface
7251c0b2f7Stbbdev TEST_CASE("join_node copy constructor"){
73de0109beSIlya Mishin oneapi::tbb::flow::graph g;
74de0109beSIlya Mishin oneapi::tbb::flow::continue_node<int> node0( g,
__anon798ebb9d0102(oneapi::tbb::flow::continue_msg) 75de0109beSIlya Mishin [](oneapi::tbb::flow::continue_msg) { return 1; } );
76de0109beSIlya Mishin
77de0109beSIlya Mishin oneapi::tbb::flow::join_node<std::tuple<int>> node1(g);
78de0109beSIlya Mishin conformance::test_push_receiver<std::tuple<int>> node2(g);
79de0109beSIlya Mishin conformance::test_push_receiver<std::tuple<int>> node3(g);
80de0109beSIlya Mishin
81de0109beSIlya Mishin oneapi::tbb::flow::make_edge(node0, oneapi::tbb::flow::input_port<0>(node1));
82de0109beSIlya Mishin oneapi::tbb::flow::make_edge(node1, node2);
83de0109beSIlya Mishin oneapi::tbb::flow::join_node<std::tuple<int>> node_copy(node1);
84de0109beSIlya Mishin
85de0109beSIlya Mishin oneapi::tbb::flow::make_edge(node_copy, node3);
86de0109beSIlya Mishin
87de0109beSIlya Mishin oneapi::tbb::flow::input_port<0>(node_copy).try_put(1);
88de0109beSIlya Mishin g.wait_for_all();
89de0109beSIlya Mishin
90de0109beSIlya Mishin auto values = conformance::get_values(node3);
91de0109beSIlya Mishin CHECK_MESSAGE((conformance::get_values(node2).size() == 0 && values.size() == 1), "Copied node doesn`t copy successor");
92de0109beSIlya Mishin
93de0109beSIlya Mishin node0.try_put(oneapi::tbb::flow::continue_msg());
94de0109beSIlya Mishin g.wait_for_all();
95de0109beSIlya Mishin
96de0109beSIlya Mishin CHECK_MESSAGE((conformance::get_values(node2).size() == 1 && conformance::get_values(node3).size() == 0), "Copied node doesn`t copy predecessor");
97de0109beSIlya Mishin
98de0109beSIlya Mishin oneapi::tbb::flow::remove_edge(node1, node2);
99de0109beSIlya Mishin oneapi::tbb::flow::input_port<0>(node1).try_put(1);
100de0109beSIlya Mishin g.wait_for_all();
101de0109beSIlya Mishin oneapi::tbb::flow::join_node<std::tuple<int>> node_copy2(node1);
102de0109beSIlya Mishin oneapi::tbb::flow::make_edge(node_copy2, node3);
103de0109beSIlya Mishin oneapi::tbb::flow::input_port<0>(node_copy2).try_put(2);
104de0109beSIlya Mishin g.wait_for_all();
105de0109beSIlya Mishin CHECK_MESSAGE((std::get<0>(conformance::get_values(node3)[0]) == 2), "Copied node doesn`t copy messages in the input ports");
10651c0b2f7Stbbdev }
10751c0b2f7Stbbdev
10851c0b2f7Stbbdev //! Test inheritance relations
10951c0b2f7Stbbdev //! \brief \ref interface
11051c0b2f7Stbbdev TEST_CASE("join_node inheritance"){
111de0109beSIlya Mishin CHECK_MESSAGE((std::is_base_of<oneapi::tbb::flow::graph_node,
112de0109beSIlya Mishin oneapi::tbb::flow::join_node<my_input_tuple>>::value),
113de0109beSIlya Mishin "join_node should be derived from graph_node");
114de0109beSIlya Mishin CHECK_MESSAGE((std::is_base_of<oneapi::tbb::flow::sender<my_input_tuple>,
115de0109beSIlya Mishin oneapi::tbb::flow::join_node<my_input_tuple>>::value),
116de0109beSIlya Mishin "join_node should be derived from sender<input_tuple>");
11751c0b2f7Stbbdev }
11851c0b2f7Stbbdev
119de0109beSIlya Mishin //! Test join_node<queueing> behavior and broadcast property
12051c0b2f7Stbbdev //! \brief \ref requirement
121de0109beSIlya Mishin TEST_CASE("join_node queueing policy and broadcast property") {
122de0109beSIlya Mishin oneapi::tbb::flow::graph g;
123de0109beSIlya Mishin oneapi::tbb::flow::function_node<int, int>
__anon798ebb9d0202(const int &i) 124de0109beSIlya Mishin f1( g, oneapi::tbb::flow::unlimited, [](const int &i) { return i; } );
125de0109beSIlya Mishin oneapi::tbb::flow::function_node<float, float>
__anon798ebb9d0302(const float &f) 126de0109beSIlya Mishin f2( g, oneapi::tbb::flow::unlimited, [](const float &f) { return f; } );
127de0109beSIlya Mishin oneapi::tbb::flow::continue_node<input_msg> c1( g,
__anon798ebb9d0402(oneapi::tbb::flow::continue_msg) 128de0109beSIlya Mishin [](oneapi::tbb::flow::continue_msg) { return input_msg(1); } );
12951c0b2f7Stbbdev
130de0109beSIlya Mishin oneapi::tbb::flow::join_node<my_input_tuple, oneapi::tbb::flow::queueing> testing_node(g);
13151c0b2f7Stbbdev
132de0109beSIlya Mishin conformance::test_push_receiver<my_input_tuple> q_node(g);
133de0109beSIlya Mishin
134de0109beSIlya Mishin std::atomic<int> number{1};
135de0109beSIlya Mishin oneapi::tbb::flow::function_node<my_input_tuple, my_input_tuple>
136de0109beSIlya Mishin f3( g, oneapi::tbb::flow::unlimited,
__anon798ebb9d0502( const my_input_tuple &t ) 137de0109beSIlya Mishin [&]( const my_input_tuple &t ) {
138de0109beSIlya Mishin CHECK_MESSAGE((std::get<0>(t) == number), "Messages must be in first-in first-out order" );
139de0109beSIlya Mishin CHECK_MESSAGE((std::get<1>(t) == static_cast<float>(number) + 0.5f), "Messages must be in first-in first-out order" );
140de0109beSIlya Mishin CHECK_MESSAGE((std::get<2>(t) == 1), "Messages must be in first-in first-out order" );
141de0109beSIlya Mishin ++number;
142de0109beSIlya Mishin return t;
14351c0b2f7Stbbdev } );
14451c0b2f7Stbbdev
145de0109beSIlya Mishin oneapi::tbb::flow::make_edge(f1, oneapi::tbb::flow::input_port<0>(testing_node));
146de0109beSIlya Mishin oneapi::tbb::flow::make_edge(f2, oneapi::tbb::flow::input_port<1>(testing_node));
147de0109beSIlya Mishin oneapi::tbb::flow::make_edge(c1, oneapi::tbb::flow::input_port<2>(testing_node));
148de0109beSIlya Mishin make_edge(testing_node, f3);
149de0109beSIlya Mishin make_edge(f3, q_node);
15051c0b2f7Stbbdev
151de0109beSIlya Mishin f1.try_put(1);
152de0109beSIlya Mishin g.wait_for_all();
153de0109beSIlya Mishin CHECK_MESSAGE((get_values(q_node).size() == 0),
154de0109beSIlya Mishin "join_node must broadcast when there is at least one message at each input port");
155de0109beSIlya Mishin f1.try_put(2);
156de0109beSIlya Mishin f2.try_put(1.5f);
157de0109beSIlya Mishin g.wait_for_all();
158de0109beSIlya Mishin CHECK_MESSAGE((get_values(q_node).size() == 0),
159de0109beSIlya Mishin "join_node must broadcast when there is at least one message at each input port");
16051c0b2f7Stbbdev f1.try_put(3);
161de0109beSIlya Mishin f2.try_put(2.5f);
162de0109beSIlya Mishin c1.try_put(oneapi::tbb::flow::continue_msg());
16351c0b2f7Stbbdev g.wait_for_all();
164de0109beSIlya Mishin CHECK_MESSAGE((get_values(q_node).size() == 1),
165de0109beSIlya Mishin "join_node must broadcast when there is at least one message at each input port");
166de0109beSIlya Mishin f2.try_put(3.5f);
167de0109beSIlya Mishin c1.try_put(oneapi::tbb::flow::continue_msg());
168de0109beSIlya Mishin g.wait_for_all();
169de0109beSIlya Mishin CHECK_MESSAGE((get_values(q_node).size() == 1),
170de0109beSIlya Mishin "If at least one successor accepts the tuple, the head of each input port’s queue is removed");
171de0109beSIlya Mishin c1.try_put(oneapi::tbb::flow::continue_msg());
172de0109beSIlya Mishin g.wait_for_all();
173de0109beSIlya Mishin CHECK_MESSAGE((get_values(q_node).size() == 1),
174de0109beSIlya Mishin "If at least one successor accepts the tuple, the head of each input port’s queue is removed");
175de0109beSIlya Mishin c1.try_put(oneapi::tbb::flow::continue_msg());
176de0109beSIlya Mishin g.wait_for_all();
177de0109beSIlya Mishin CHECK_MESSAGE((get_values(q_node).size() == 0),
178de0109beSIlya Mishin "join_node must broadcast when there is at least one message at each input port");
179de0109beSIlya Mishin
180de0109beSIlya Mishin oneapi::tbb::flow::remove_edge(testing_node, f3);
181de0109beSIlya Mishin
182de0109beSIlya Mishin f1.try_put(1);
183de0109beSIlya Mishin f2.try_put(1);
184de0109beSIlya Mishin c1.try_put(oneapi::tbb::flow::continue_msg());
185de0109beSIlya Mishin g.wait_for_all();
186de0109beSIlya Mishin
187de0109beSIlya Mishin my_input_tuple tmp(0, 0.f, input_msg(0));
188de0109beSIlya Mishin CHECK_MESSAGE((testing_node.try_get(tmp)), "If no one successor accepts the tuple the messages\
189de0109beSIlya Mishin must remain in their respective input port queues");
190de0109beSIlya Mishin CHECK_MESSAGE((tmp == my_input_tuple(1, 1.f, input_msg(1))), "If no one successor accepts the tuple\
191de0109beSIlya Mishin the messages must remain in their respective input port queues");
19251c0b2f7Stbbdev }
19351c0b2f7Stbbdev
194de0109beSIlya Mishin //! Test join_node<reserving> behavior
19551c0b2f7Stbbdev //! \brief \ref requirement
196de0109beSIlya Mishin TEST_CASE("join_node reserving policy") {
197de0109beSIlya Mishin conformance::test_with_reserving_join_node_class<oneapi::tbb::flow::write_once_node<int>>();
19851c0b2f7Stbbdev }
19951c0b2f7Stbbdev
200de0109beSIlya Mishin template<typename KeyType>
201de0109beSIlya Mishin struct MyHash{
hashMyHash202de0109beSIlya Mishin std::size_t hash(const KeyType &k) const {
203de0109beSIlya Mishin return k * 2000 + 3;
204de0109beSIlya Mishin }
205de0109beSIlya Mishin
equalMyHash206de0109beSIlya Mishin bool equal(const KeyType &k1, const KeyType &k2) const{
207de0109beSIlya Mishin return hash(k1) == hash(k2);
208de0109beSIlya Mishin }
209de0109beSIlya Mishin };
210de0109beSIlya Mishin
211de0109beSIlya Mishin //! Test join_node<key_matching> behavior
21251c0b2f7Stbbdev //! \brief \ref requirement
213de0109beSIlya Mishin TEST_CASE("join_node key_matching policy"){
214de0109beSIlya Mishin oneapi::tbb::flow::graph g;
__anon798ebb9d0602(const oneapi::tbb::flow::continue_msg &) 215de0109beSIlya Mishin auto body1 = [](const oneapi::tbb::flow::continue_msg &) -> int { return 1; };
__anon798ebb9d0702(const float &val) 216de0109beSIlya Mishin auto body2 = [](const float &val) -> int { return static_cast<int>(val); };
21751c0b2f7Stbbdev
218de0109beSIlya Mishin oneapi::tbb::flow::join_node<std::tuple<oneapi::tbb::flow::continue_msg, float>,
219de0109beSIlya Mishin oneapi::tbb::flow::key_matching<int, MyHash<int>>> testing_node(g, body1, body2);
22051c0b2f7Stbbdev
221de0109beSIlya Mishin oneapi::tbb::flow::input_port<0>(testing_node).try_put(oneapi::tbb::flow::continue_msg());
222de0109beSIlya Mishin oneapi::tbb::flow::input_port<1>(testing_node).try_put(1.3f);
22351c0b2f7Stbbdev
22451c0b2f7Stbbdev g.wait_for_all();
22551c0b2f7Stbbdev
226de0109beSIlya Mishin std::tuple<oneapi::tbb::flow::continue_msg, float> tmp;
227de0109beSIlya Mishin CHECK_MESSAGE((testing_node.try_get(tmp)), "Mapped keys should match.\
228de0109beSIlya Mishin If no successor accepts the tuple, it is must been saved and will be forwarded on a subsequent try_get");
229de0109beSIlya Mishin CHECK_MESSAGE((!testing_node.try_get(tmp)), "Message should not exist after item is consumed");
230de0109beSIlya Mishin }
231de0109beSIlya Mishin
232de0109beSIlya Mishin //! Test join_node<tag_matching> behavior
233de0109beSIlya Mishin //! \brief \ref requirement
234de0109beSIlya Mishin TEST_CASE("join_node tag_matching policy"){
235de0109beSIlya Mishin oneapi::tbb::flow::graph g;
__anon798ebb9d0802(const oneapi::tbb::flow::continue_msg &) 236de0109beSIlya Mishin auto body1 = [](const oneapi::tbb::flow::continue_msg &) -> oneapi::tbb::flow::tag_value { return 1; };
__anon798ebb9d0902(const float &val) 237de0109beSIlya Mishin auto body2 = [](const float &val) -> oneapi::tbb::flow::tag_value { return static_cast<oneapi::tbb::flow::tag_value>(val); };
238de0109beSIlya Mishin
239de0109beSIlya Mishin oneapi::tbb::flow::join_node<std::tuple<oneapi::tbb::flow::continue_msg, float>,
240de0109beSIlya Mishin oneapi::tbb::flow::tag_matching> testing_node(g, body1, body2);
241de0109beSIlya Mishin
242de0109beSIlya Mishin oneapi::tbb::flow::input_port<0>(testing_node).try_put(oneapi::tbb::flow::continue_msg());
243de0109beSIlya Mishin oneapi::tbb::flow::input_port<1>(testing_node).try_put(1.3f);
244de0109beSIlya Mishin
245de0109beSIlya Mishin g.wait_for_all();
246de0109beSIlya Mishin
247de0109beSIlya Mishin std::tuple<oneapi::tbb::flow::continue_msg, float> tmp;
248de0109beSIlya Mishin CHECK_MESSAGE((testing_node.try_get(tmp) == true), "Mapped keys should match");
249de0109beSIlya Mishin }
250de0109beSIlya Mishin
251de0109beSIlya Mishin #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
252de0109beSIlya Mishin //! Test deduction guides
253de0109beSIlya Mishin //! \brief \ref requirement
254de0109beSIlya Mishin TEST_CASE("Deduction guides test"){
255de0109beSIlya Mishin test_deduction_guides();
256de0109beSIlya Mishin }
257de0109beSIlya Mishin #endif
258de0109beSIlya Mishin
259de0109beSIlya Mishin //! Test join_node input_ports() returns a tuple of input ports.
260de0109beSIlya Mishin //! \brief \ref interface \ref requirement
261de0109beSIlya Mishin TEST_CASE("join_node output_ports") {
262de0109beSIlya Mishin oneapi::tbb::flow::graph g;
263de0109beSIlya Mishin oneapi::tbb::flow::join_node<std::tuple<int>> node(g);
264de0109beSIlya Mishin
265de0109beSIlya Mishin CHECK_MESSAGE((std::is_same<oneapi::tbb::flow::join_node<std::tuple<int>>::input_ports_type&,
266de0109beSIlya Mishin decltype(node.input_ports())>::value), "join_node input_ports should returns a tuple of input ports");
26751c0b2f7Stbbdev }
268*a088cfa0SKonstantin Boyarinov
269*a088cfa0SKonstantin Boyarinov #if __TBB_CPP17_INVOKE_PRESENT
270*a088cfa0SKonstantin Boyarinov
271*a088cfa0SKonstantin Boyarinov template <typename K, typename Body1, typename Body2>
test_invoke_basic(Body1 body1,Body2 body2)272*a088cfa0SKonstantin Boyarinov void test_invoke_basic(Body1 body1, Body2 body2) {
273*a088cfa0SKonstantin Boyarinov static_assert(std::is_same_v<std::decay_t<K>, std::size_t>, "incorrect test setup");
274*a088cfa0SKonstantin Boyarinov using namespace oneapi::tbb::flow;
275*a088cfa0SKonstantin Boyarinov auto generator = [](std::size_t n) { return test_invoke::SmartID<std::size_t>(n); };
276*a088cfa0SKonstantin Boyarinov graph g;
277*a088cfa0SKonstantin Boyarinov
278*a088cfa0SKonstantin Boyarinov function_node<std::size_t, test_invoke::SmartID<std::size_t>> f1(g, unlimited, generator);
279*a088cfa0SKonstantin Boyarinov function_node<std::size_t, test_invoke::SmartID<std::size_t>> f2(g, unlimited, generator);
280*a088cfa0SKonstantin Boyarinov
281*a088cfa0SKonstantin Boyarinov using tuple_type = std::tuple<test_invoke::SmartID<std::size_t>, test_invoke::SmartID<std::size_t>>;
282*a088cfa0SKonstantin Boyarinov using join_type = join_node<tuple_type, key_matching<K>>;
283*a088cfa0SKonstantin Boyarinov
284*a088cfa0SKonstantin Boyarinov
285*a088cfa0SKonstantin Boyarinov join_type j(g, body1, body2);
286*a088cfa0SKonstantin Boyarinov
287*a088cfa0SKonstantin Boyarinov buffer_node<tuple_type> buf(g);
288*a088cfa0SKonstantin Boyarinov
289*a088cfa0SKonstantin Boyarinov make_edge(f1, input_port<0>(j));
290*a088cfa0SKonstantin Boyarinov make_edge(f2, input_port<1>(j));
291*a088cfa0SKonstantin Boyarinov make_edge(j, buf);
292*a088cfa0SKonstantin Boyarinov
293*a088cfa0SKonstantin Boyarinov std::size_t objects_count = 100;
294*a088cfa0SKonstantin Boyarinov for (std::size_t i = 0; i < objects_count; ++i) {
295*a088cfa0SKonstantin Boyarinov f1.try_put(i);
296*a088cfa0SKonstantin Boyarinov f2.try_put(objects_count - i - 1);
297*a088cfa0SKonstantin Boyarinov }
298*a088cfa0SKonstantin Boyarinov
299*a088cfa0SKonstantin Boyarinov g.wait_for_all();
300*a088cfa0SKonstantin Boyarinov
301*a088cfa0SKonstantin Boyarinov std::size_t buf_size = 0;
302*a088cfa0SKonstantin Boyarinov tuple_type tpl;
303*a088cfa0SKonstantin Boyarinov
304*a088cfa0SKonstantin Boyarinov while(buf.try_get(tpl)) {
305*a088cfa0SKonstantin Boyarinov ++buf_size;
306*a088cfa0SKonstantin Boyarinov CHECK(std::get<0>(tpl).id == std::get<1>(tpl).id);
307*a088cfa0SKonstantin Boyarinov }
308*a088cfa0SKonstantin Boyarinov CHECK(buf_size == objects_count);
309*a088cfa0SKonstantin Boyarinov }
310*a088cfa0SKonstantin Boyarinov
311*a088cfa0SKonstantin Boyarinov //! Test that key_matching join_node uses std::invoke to run the body
312*a088cfa0SKonstantin Boyarinov //! \brief \ref requirement
313*a088cfa0SKonstantin Boyarinov TEST_CASE("key_matching join_node invoke semantics") {
314*a088cfa0SKonstantin Boyarinov test_invoke_basic</*K = */std::size_t>(&test_invoke::SmartID<std::size_t>::get_id, &test_invoke::SmartID<std::size_t>::id);
315*a088cfa0SKonstantin Boyarinov test_invoke_basic</*K = */const std::size_t&>(&test_invoke::SmartID<std::size_t>::get_id_ref, &test_invoke::SmartID<std::size_t>::get_id_ref);
316*a088cfa0SKonstantin Boyarinov }
317*a088cfa0SKonstantin Boyarinov #endif // __TBB_CPP17_INVOKE_PRESENT
318