1 /*
2     Copyright (c) 2020-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 #ifndef __TBB_flow_graph_node_set_impl_H
18 #define __TBB_flow_graph_node_set_impl_H
19 
20 #ifndef __TBB_flow_graph_H
21 #error Do not #include this internal file directly; use public TBB headers instead.
22 #endif
23 
24 // Included in namespace tbb::detail::d1 (in flow_graph.h)
25 
26 #if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
27 // Visual Studio 2019 reports an error while calling predecessor_selector::get and successor_selector::get
28 // Seems like the well-formed expression in trailing decltype is treated as ill-formed
29 // TODO: investigate problems with decltype in trailing return types or find the cross-platform solution
30 #define __TBB_MSVC_DISABLE_TRAILING_DECLTYPE (_MSC_VER >= 1900)
31 
32 namespace order {
33 struct undefined {};
34 struct following {};
35 struct preceding {};
36 }
37 
38 class get_graph_helper {
39 public:
40     // TODO: consider making graph_reference() public and consistent interface to get a reference to the graph
41     // and remove get_graph_helper
42     template <typename T>
get(const T & object)43     static graph& get(const T& object) {
44         return get_impl(object, std::is_base_of<graph_node, T>());
45     }
46 
47 private:
48     // Get graph from the object of type derived from graph_node
49     template <typename T>
get_impl(const T & object,std::true_type)50     static graph& get_impl(const T& object, std::true_type) {
51         return static_cast<const graph_node*>(&object)->my_graph;
52     }
53 
54     template <typename T>
get_impl(const T & object,std::false_type)55     static graph& get_impl(const T& object, std::false_type) {
56         return object.graph_reference();
57     }
58 };
59 
60 template<typename Order, typename... Nodes>
61 struct node_set {
62     typedef Order order_type;
63 
64     std::tuple<Nodes&...> nodes;
node_setnode_set65     node_set(Nodes&... ns) : nodes(ns...) {}
66 
67     template <typename... Nodes2>
node_setnode_set68     node_set(const node_set<order::undefined, Nodes2...>& set) : nodes(set.nodes) {}
69 
graph_referencenode_set70     graph& graph_reference() const {
71         return get_graph_helper::get(std::get<0>(nodes));
72     }
73 };
74 
75 namespace alias_helpers {
76 template <typename T> using output_type = typename T::output_type;
77 template <typename T> using output_ports_type = typename T::output_ports_type;
78 template <typename T> using input_type = typename T::input_type;
79 template <typename T> using input_ports_type = typename T::input_ports_type;
80 } // namespace alias_helpers
81 
82 template <typename T>
83 using has_output_type = supports<T, alias_helpers::output_type>;
84 
85 template <typename T>
86 using has_input_type = supports<T, alias_helpers::input_type>;
87 
88 template <typename T>
89 using has_input_ports_type = supports<T, alias_helpers::input_ports_type>;
90 
91 template <typename T>
92 using has_output_ports_type = supports<T, alias_helpers::output_ports_type>;
93 
94 template<typename T>
95 struct is_sender : std::is_base_of<sender<typename T::output_type>, T> {};
96 
97 template<typename T>
98 struct is_receiver : std::is_base_of<receiver<typename T::input_type>, T> {};
99 
100 template <typename Node>
101 struct is_async_node : std::false_type {};
102 
103 template <typename... Args>
104 struct is_async_node<async_node<Args...>> : std::true_type {};
105 
106 template<typename FirstPredecessor, typename... Predecessors>
107 node_set<order::following, FirstPredecessor, Predecessors...>
108 follows(FirstPredecessor& first_predecessor, Predecessors&... predecessors) {
109     static_assert((conjunction<has_output_type<FirstPredecessor>,
110                                                    has_output_type<Predecessors>...>::value),
111                         "Not all node's predecessors has output_type typedef");
112     static_assert((conjunction<is_sender<FirstPredecessor>, is_sender<Predecessors>...>::value),
113                         "Not all node's predecessors are senders");
114     return node_set<order::following, FirstPredecessor, Predecessors...>(first_predecessor, predecessors...);
115 }
116 
117 template<typename... Predecessors>
118 node_set<order::following, Predecessors...>
119 follows(node_set<order::undefined, Predecessors...>& predecessors_set) {
120     static_assert((conjunction<has_output_type<Predecessors>...>::value),
121                         "Not all nodes in the set has output_type typedef");
122     static_assert((conjunction<is_sender<Predecessors>...>::value),
123                         "Not all nodes in the set are senders");
124     return node_set<order::following, Predecessors...>(predecessors_set);
125 }
126 
127 template<typename FirstSuccessor, typename... Successors>
128 node_set<order::preceding, FirstSuccessor, Successors...>
129 precedes(FirstSuccessor& first_successor, Successors&... successors) {
130     static_assert((conjunction<has_input_type<FirstSuccessor>,
131                                                     has_input_type<Successors>...>::value),
132                         "Not all node's successors has input_type typedef");
133     static_assert((conjunction<is_receiver<FirstSuccessor>, is_receiver<Successors>...>::value),
134                         "Not all node's successors are receivers");
135     return node_set<order::preceding, FirstSuccessor, Successors...>(first_successor, successors...);
136 }
137 
138 template<typename... Successors>
139 node_set<order::preceding, Successors...>
140 precedes(node_set<order::undefined, Successors...>& successors_set) {
141     static_assert((conjunction<has_input_type<Successors>...>::value),
142                         "Not all nodes in the set has input_type typedef");
143     static_assert((conjunction<is_receiver<Successors>...>::value),
144                         "Not all nodes in the set are receivers");
145     return node_set<order::preceding, Successors...>(successors_set);
146 }
147 
148 template <typename Node, typename... Nodes>
149 node_set<order::undefined, Node, Nodes...>
150 make_node_set(Node& first_node, Nodes&... nodes) {
151     return node_set<order::undefined, Node, Nodes...>(first_node, nodes...);
152 }
153 
154 template<size_t I>
155 class successor_selector {
156     template <typename NodeType>
157     static auto get_impl(NodeType& node, std::true_type) -> decltype(input_port<I>(node)) {
158         return input_port<I>(node);
159     }
160 
161     template <typename NodeType>
162     static NodeType& get_impl(NodeType& node, std::false_type) { return node; }
163 
164 public:
165     template <typename NodeType>
166 #if __TBB_MSVC_DISABLE_TRAILING_DECLTYPE
167     static auto& get(NodeType& node)
168 #else
169     static auto get(NodeType& node) -> decltype(get_impl(node, has_input_ports_type<NodeType>()))
170 #endif
171     {
172         return get_impl(node, has_input_ports_type<NodeType>());
173     }
174 };
175 
176 template<size_t I>
177 class predecessor_selector {
178     template <typename NodeType>
179     static auto internal_get(NodeType& node, std::true_type) -> decltype(output_port<I>(node)) {
180         return output_port<I>(node);
181     }
182 
183     template <typename NodeType>
184     static NodeType& internal_get(NodeType& node, std::false_type) { return node;}
185 
186     template <typename NodeType>
187 #if __TBB_MSVC_DISABLE_TRAILING_DECLTYPE
188     static auto& get_impl(NodeType& node, std::false_type)
189 #else
190     static auto get_impl(NodeType& node, std::false_type) -> decltype(internal_get(node, has_output_ports_type<NodeType>()))
191 #endif
192     {
193         return internal_get(node, has_output_ports_type<NodeType>());
194     }
195 
196     template <typename AsyncNode>
197     static AsyncNode& get_impl(AsyncNode& node, std::true_type) { return node; }
198 
199 public:
200     template <typename NodeType>
201 #if __TBB_MSVC_DISABLE_TRAILING_DECLTYPE
202     static auto& get(NodeType& node)
203 #else
204     static auto get(NodeType& node) -> decltype(get_impl(node, is_async_node<NodeType>()))
205 #endif
206     {
207         return get_impl(node, is_async_node<NodeType>());
208     }
209 };
210 
211 template<size_t I>
212 class make_edges_helper {
213 public:
214     template<typename PredecessorsTuple, typename NodeType>
215     static void connect_predecessors(PredecessorsTuple& predecessors, NodeType& node) {
216         make_edge(std::get<I>(predecessors), successor_selector<I>::get(node));
217         make_edges_helper<I - 1>::connect_predecessors(predecessors, node);
218     }
219 
220     template<typename SuccessorsTuple, typename NodeType>
221     static void connect_successors(NodeType& node, SuccessorsTuple& successors) {
222         make_edge(predecessor_selector<I>::get(node), std::get<I>(successors));
223         make_edges_helper<I - 1>::connect_successors(node, successors);
224     }
225 };
226 
227 template<>
228 struct make_edges_helper<0> {
229     template<typename PredecessorsTuple, typename NodeType>
230     static void connect_predecessors(PredecessorsTuple& predecessors, NodeType& node) {
231         make_edge(std::get<0>(predecessors), successor_selector<0>::get(node));
232     }
233 
234     template<typename SuccessorsTuple, typename NodeType>
235     static void connect_successors(NodeType& node, SuccessorsTuple& successors) {
236         make_edge(predecessor_selector<0>::get(node), std::get<0>(successors));
237     }
238 };
239 
240 // TODO: consider adding an overload for making edges between node sets
241 template<typename NodeType, typename OrderFlagType, typename... Args>
242 void make_edges(const node_set<OrderFlagType, Args...>& s, NodeType& node) {
243     const std::size_t SetSize = std::tuple_size<decltype(s.nodes)>::value;
244     make_edges_helper<SetSize - 1>::connect_predecessors(s.nodes, node);
245 }
246 
247 template <typename NodeType, typename OrderFlagType, typename... Args>
248 void make_edges(NodeType& node, const node_set<OrderFlagType, Args...>& s) {
249     const std::size_t SetSize = std::tuple_size<decltype(s.nodes)>::value;
250     make_edges_helper<SetSize - 1>::connect_successors(node, s.nodes);
251 }
252 
253 template <typename NodeType, typename... Nodes>
254 void make_edges_in_order(const node_set<order::following, Nodes...>& ns, NodeType& node) {
255     make_edges(ns, node);
256 }
257 
258 template <typename NodeType, typename... Nodes>
259 void make_edges_in_order(const node_set<order::preceding, Nodes...>& ns, NodeType& node) {
260     make_edges(node, ns);
261 }
262 
263 #endif  // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
264 
265 #endif // __TBB_flow_graph_node_set_impl_H
266