Sygaldry
Loading...
Searching...
No Matches
sygac-components.hpp
1#pragma once
2/*
3Copyright 2023 Travis J. West, https://traviswest.ca, Input Devices and Music Interaction Laboratory
4(IDMIL), Centre for Interdisciplinary Research in Music Media and Technology
5(CIRMMT), McGill University, Montréal, Canada, and Univ. Lille, Inria, CNRS,
6Centrale Lille, UMR 9189 CRIStAL, F-59000 Lille, France
7
8SPDX-License-Identifier: MIT
9*/
10
11#include <utility>
12#include <boost/pfr.hpp>
13#include <boost/mp11.hpp>
14#include "sygac-tuple.hpp"
15#include "sygac-metadata.hpp"
16#include "sygac-functions.hpp"
17#include "sygac-endpoints.hpp"
18
19namespace sygaldry {
20
21using boost::mp11::mp_transform;
22
26
30
39template<typename T>
41 = not std::is_union_v<T>
42 && ( std::is_aggregate_v<T>
43 || std::is_scalar_v<T>
44 )
45 ;
46
48template <typename T> struct main_subroutine_reflection {using exists = std::false_type;};
49
51template <typename T>
52 requires (std::same_as<void, typename function_reflection<&T::operator()>::return_type>
53 && !std::same_as<void, typename function_reflection<&T::main>::return_type>)
55
57template <typename T>
58 requires std::same_as<void, typename function_reflection<&T::main>::return_type>
59struct main_subroutine_reflection<T> : function_reflection<&T::main> {};
61template <typename T> struct init_subroutine_reflection {using exists = std::false_type;};
62
64template <typename T>
65 requires std::same_as<void, typename function_reflection<&T::init>::return_type>
67
69template <typename T> struct external_sources_subroutine_reflection {using exists = std::false_type;};
70
72template <typename T>
73 requires std::same_as<void, typename function_reflection<&T::external_sources>::return_type>
75
77template <typename T> struct external_destinations_subroutine_reflection {using exists = std::false_type;};
78
80template <typename T>
81 requires std::same_as<void, typename function_reflection<&T::external_destinations>::return_type>
82struct external_destinations_subroutine_reflection<T> : function_reflection<&T::external_destinations> {};
83template<typename T>
84concept has_main_subroutine // = main_subroutine_reflection<T>::exists::value;
85 = std::same_as<void, typename function_reflection<&T::operator()>::return_type>
86 || std::same_as<void, typename function_reflection<&T::main>::return_type>
87 ;
88template<typename T>
90 = std::same_as<void, typename function_reflection<&T::init>::return_type>;
91template<typename T>
93 = std::same_as<void, typename function_reflection<&T::external_sources>::return_type>;
94template<typename T>
96 = std::same_as<void, typename function_reflection<&T::external_destinations>::return_type>;
97
98#define has_type_or_value(NAME)\
99template<typename T> concept has_##NAME = requires (T t)\
100{\
101 t.NAME;\
102 requires SimpleAggregate<decltype(t.NAME)>;\
103};\
104\
105template<has_##NAME T> struct type_of_##NAME\
106{\
107 using type = decltype(std::declval<T>().NAME);\
108};\
109\
110template<typename T> using NAME##_t = typename type_of_##NAME<T>::type;\
111\
112template<typename T> requires has_##NAME<T> constexpr auto& NAME##_of(T& t) { return t.NAME; }\
113template<typename T> requires has_##NAME<T> constexpr const auto& NAME##_of(const T& t) { return t.NAME; }\
114
115has_type_or_value(inputs);
116has_type_or_value(outputs);
117
118#undef has_type_or_value
119
120
122template<typename T>
124 = has_name<T>
129 || has_inputs<T>
130 || has_outputs<T>
131 )
132 ;
133
134namespace detail {
135
137template<typename T>
138struct assembly_predicate : std::false_type {};
140template<typename T>
141 requires Component<T>
142struct assembly_predicate<T> : std::true_type {};
143
144template<typename T>
145 requires Tuple<T>
146struct assembly_predicate<T> : std::false_type {};
147
149template<typename T>
150 requires SimpleAggregate<T> && (not Component<T>) && (not std::is_scalar_v<T>) && (not Tuple<T>)
151struct assembly_predicate<T>
152{
153 using tup = decltype(boost::pfr::structure_to_tuple(std::declval<T&>()));
154 using valid_components = boost::mp11::mp_transform<assembly_predicate, tup>;
155 using all_valid = boost::mp11::mp_apply<boost::mp11::mp_all, valid_components>;
156 static constexpr bool value = all_valid::value;
157};
158}
159
160template<typename T>
161concept Assembly = detail::assembly_predicate<T>::value && not Component<T> && not Tuple<T>;
162
163namespace node
164{
165 struct assembly {};
166 struct component {};
170 struct input_endpoint {};
172 struct endpoint {};
173 template<typename> struct is_assembly : std::false_type {};
174 template<> struct is_assembly<assembly> : std::true_type {};
175 template<typename> struct is_component : std::false_type {};
176 template<> struct is_component<component> : std::true_type {};
177 template<typename> struct is_inputs_container : std::false_type {};
178 template<> struct is_inputs_container<inputs_container> : std::true_type {};
179 template<typename> struct is_outputs_container : std::false_type {};
180 template<> struct is_outputs_container<outputs_container> : std::true_type {};
181 template<typename> struct is_input_endpoint : std::false_type {};
182 template<> struct is_input_endpoint<input_endpoint> : std::true_type {};
183 template<typename> struct is_output_endpoint : std::false_type {};
184 template<> struct is_output_endpoint<output_endpoint> : std::true_type {};
185 template<typename> struct is_endpoints_container : std::false_type {};
186 template<> struct is_endpoints_container<inputs_container> : std::true_type {};
187 template<> struct is_endpoints_container<outputs_container> : std::true_type {};
188 template<> struct is_endpoints_container<endpoints_container> : std::true_type {};
189 template<typename> struct is_endpoint : std::false_type {};
190 template<> struct is_endpoint<input_endpoint> : std::true_type {};
191 template<> struct is_endpoint<output_endpoint> : std::true_type {};
192 template<> struct is_endpoint<endpoint> : std::true_type {};
193}
194
195template<typename Tag, typename Val>
196struct tagged
197{
198 using tag = Tag;
199 using type = Val;
200 Val& ref;
201};
202template<typename Tag, typename T>
203constexpr auto endpoint_subtree(T& component)
204{
205 using ContainerTag = boost::mp11::mp_if_c< std::same_as<Tag, node::input_endpoint>
208 >;
209
210 constexpr auto f = []<typename Container>(Container& container)
211 {
212 auto endpoints = sygaldry::pfr::structure_tie(container);
213 auto head = tpl::make_tuple(tagged<ContainerTag, Container>{container});
214 auto tail = endpoints.map([]<typename Ep>(Ep& endpoint)
215 {
216 return tpl::make_tuple(tagged<Tag, Ep>{endpoint});
217 });
218 return tpl::make_tuple(tpl::tuple_cat(head, tail));
219 };
220
221 constexpr bool inputs = std::same_as<Tag, node::input_endpoint> && has_inputs<T>;
222 constexpr bool outputs = std::same_as<Tag, node::output_endpoint> && has_outputs<T>;
223 if constexpr (inputs) return f(inputs_of(component));
224 else if constexpr (outputs) return f(outputs_of(component));
225 else return tpl::tuple<>{};
226}
227
228template<typename T>
229constexpr auto component_to_tree(T& component)
230{
231 if constexpr (Component<T>)
232 {
233 return tpl::tuple_cat( tpl::make_tuple(tagged<node::component, T>{component})
234 , endpoint_subtree<node::input_endpoint>(component)
235 , endpoint_subtree<node::output_endpoint>(component)
236 );
237 }
238 else
239 {
240 auto subcomponents = sygaldry::pfr::structure_tie(component);
241 auto head = tpl::make_tuple(tagged<node::assembly, T>{component});
242 auto tail = subcomponents.map([](auto& subcomponent)
243 {
244 return component_to_tree(subcomponent);
245 });
246 return tpl::tuple_cat( head, tail);
247 }
248}
249template<Tuple T>
250constexpr auto component_tree_to_node_list(T tree)
251{
252 if constexpr (std::tuple_size_v<T> == 0) return tree;
253 auto head = tuple_head(tree);
254 auto tail = tuple_tail(tree);
255 if constexpr (Tuple<decltype(head)>) return tpl::tuple_cat(component_tree_to_node_list(head), component_tree_to_node_list(tail));
256 else return tpl::tuple_cat(tpl::make_tuple(head), component_tree_to_node_list(tail));
257}
258template<typename T>
259constexpr auto component_to_node_list(T& component)
260{
261 return component_tree_to_node_list(component_to_tree(component));
262}
263template<template<typename>typename F>
264constexpr auto node_list_filter(Tuple auto tup)
265{
266 return tpl::apply([](auto...args)
267 {
268 auto ret = tpl::tuple_cat(args...);
269 if constexpr (std::tuple_size_v<decltype(ret)> == 0)
270 return;
271 else if constexpr (std::tuple_size_v<decltype(ret)> == 1)
272 return tpl::get<0>(ret);
273 else return ret;
274 }
275 , tup.map([]<typename E>(E element)
276 {
277 if constexpr (F<E>::value) return tpl::make_tuple(element);
278 else return tpl::tuple<>{};
279 }));
280}
281template<template<typename>typename F, typename T>
282constexpr auto component_filter(T& component)
283{
284 return node_list_filter<F>(component_to_node_list(component));
285}
286template<typename T>
288{
289 template<typename Y>
290 struct fn : std::is_same<T, typename Y::type> {};
291};
292
293template<typename T>
294constexpr auto& find(Tuple auto tup)
295{
296 return node_list_filter<tagged_is_same<T>::template fn>(tup).ref;
297}
298
299template<typename T>
300constexpr auto& find(auto& component)
301{
302 return node_list_filter<tagged_is_same<T>::template fn>(component_to_node_list(component)).ref;
303}
304template<typename ... RequestedNodes>
306{
307 template<typename Tag> using fn = boost::mp11::mp_contains<tpl::tuple<RequestedNodes...>, typename Tag::tag>;
308};
309
310template<typename ... RequestedNodes>
311constexpr auto node_list_filter_by_tag(Tuple auto tup)
312{
313 return node_list_filter<_search_by_tags<RequestedNodes...>::template fn>(tup);
314}
315
316template<typename ... RequestedNodes>
317constexpr auto component_filter_by_tag(auto& component)
318{
319 return node_list_filter<_search_by_tags<RequestedNodes...>::template fn>(component_to_node_list(component));
320}
321
322template<typename ... RequestedNodes>
323constexpr auto for_each_node_in_list(const Tuple auto node_list, auto callback)
324{
325 auto f = [&](auto tagged_node)
326 {
327 callback(tagged_node.ref, typename decltype(tagged_node)::tag{});
328 };
329 if constexpr (sizeof...(RequestedNodes) > 0)
330 {
331 constexpr auto filtered = node_list_filter_by_tag<RequestedNodes...>(node_list);
332 tuple_for_each(filtered, f);
333 }
334 else tuple_for_each(node_list, f);
335}
336template<typename T, Tuple Tup>
337constexpr auto path_of(const Tup tree)
338{
339 if constexpr (std::tuple_size_v<Tup> == 0) // tail of a leaf node
340 return tpl::tuple<>{};
341
342 // split the tuple into its head and its tail
343 auto head = tuple_head(tree);
344 auto tail_path = path_of<T>(tuple_tail(tree));
345
346 if constexpr (Tuple<decltype(head)>) // search subtrees
347 return tpl::tuple_cat(path_of<T>(head), tail_path);
348 else if constexpr (std::same_as<typename decltype(head)::type, T>) // the node that we are looking for
349 return tpl::make_tuple(head);
350 else if constexpr (std::tuple_size_v<decltype(tail_path)> > 0) // head is a parent
351 {
352 if constexpr (has_name<typename decltype(head)::type>) // prepend named parent
353 return tpl::tuple_cat(tpl::make_tuple(head), tail_path);
354 else return tail_path; // return sub-path
355 }
356 else return tpl::tuple<>{}; // node is in another subtree
357}
358
359template<typename T, typename C>
360 requires Component<C> || Assembly<C>
361constexpr auto path_of(C& component)
362{
363 return path_of<T>(component_to_tree(component));
364}
365template<typename Tag> using untagged = typename Tag::type;
366
367// TODO: shouldn't this return references?
368template<Tuple T>
369constexpr auto remove_node_tags(T tup)
370{
371 return tup.map([](auto&& tagged) -> auto& {return tagged.ref;});
372}
373template<typename T> using output_endpoints_t =
374 decltype(remove_node_tags(component_filter_by_tag<node::output_endpoint>(std::declval<T&>())));
375template<typename T> using input_endpoints_t =
376 decltype(remove_node_tags(component_filter_by_tag<node::input_endpoint>(std::declval<T&>())));
377template<typename T> using endpoints_t =
378 decltype(remove_node_tags(component_filter_by_tag<node::input_endpoint, node::output_endpoint>(std::declval<T&>())));
379template<typename T, typename C> using path_t =
380 decltype(remove_node_tags(path_of<T>(std::declval<C&>())));
381
382template<typename T, typename ... RequestedNodes>
383constexpr auto for_each_node(T& component, auto callback)
384{
385 using boost::mp11::mp_list;
386 using boost::mp11::mp_contains;
387 using boost::mp11::mp_empty;
388
389 using nodes = mp_list<RequestedNodes...>;
390
391 if constexpr (Assembly<T>)
392 {
393 if constexpr ( mp_empty<nodes>::value
394 || mp_contains<nodes, node::assembly>::value
395 ) callback(component, node::assembly{});
396 boost::pfr::for_each_field(component, [&]<typename S>(S& subcomponent)
397 {
398 for_each_node<S, RequestedNodes...>(subcomponent, callback);
399 });
400 }
401 else if constexpr (Component<T>)
402 {
403 if constexpr ( mp_empty<nodes>::value
404 || mp_contains<nodes, node::component>::value
405 ) callback(component, node::component{});
406 if constexpr (has_inputs<T>)
407 {
408 auto& inputs = inputs_of(component);
409
410 if constexpr ( mp_empty<nodes>::value
411 || mp_contains<nodes, node::inputs_container>::value
412 || mp_contains<nodes, node::endpoints_container>::value
413 ) callback(inputs, node::inputs_container{});
414
415 // iterate only if we are visiting input endpoints
416 if constexpr ( mp_empty<nodes>::value
417 || mp_contains<nodes, node::input_endpoint>::value
418 || mp_contains<nodes, node::endpoint>::value
419 ) boost::pfr::for_each_field(inputs, [&](auto& in)
420 {
421 callback(in, node::input_endpoint{});
422 });
423 }
424 if constexpr (has_outputs<T>)
425 {
426 auto& outputs = outputs_of(component);
427
428 if constexpr ( mp_empty<nodes>::value
429 || mp_contains<nodes, node::outputs_container>::value
430 || mp_contains<nodes, node::endpoints_container>::value
431 ) callback(outputs, node::outputs_container{});
432
433 // iterate only if we are visiting output endpoints
434 if constexpr ( mp_empty<nodes>::value
435 || mp_contains<nodes, node::output_endpoint>::value
436 || mp_contains<nodes, node::endpoint>::value
437 ) boost::pfr::for_each_field(outputs, [&](auto& out)
438 {
439 callback(out, node::output_endpoint{});
440 });
441 }
442 }
443}
444template <typename T> constexpr void for_each_component(T& component, auto callback)
445{
446 for_each_node<T, node::component>(component, [&](auto& c, auto) { callback(c); });
447}
448
449template<typename T> constexpr void for_each_endpoint(T& component, auto callback)
450{
451 for_each_node<T, node::endpoint>(component, [&](auto& c, auto) { callback(c); });
452}
453
454template<typename T> constexpr void for_each_input(T& component, auto callback)
455{
456 for_each_node<T, node::input_endpoint>(component, [&](auto& c, auto) { callback(c); });
457}
458
459template<typename T> constexpr void for_each_output(T& component, auto callback)
460{
461 for_each_node<T, node::output_endpoint>(component, [&](auto& c, auto) { callback(c); });
462}
463
464template<typename Y>
465void clear_flag(Y& endpoint)
466{
467 if constexpr (ClearableFlag<Y>) clear_flag(endpoint);
468}
469
470void clear_flags(auto& component)
471{
472 for_each_endpoint(component, [](auto& endpoint) { clear_flag(endpoint); });
473}
474
475void clear_output_flags(auto& component)
476{
477 for_each_output(component, [](auto& endpoint) { clear_flag(endpoint); });
478}
479
480void clear_input_flags(auto& component)
481{
482 for_each_input(component, [](auto& endpoint) { clear_flag(endpoint); });
483}
484
485template<Component T>
486void init(T& component)
487{
488 if constexpr (requires {component.init();}) component.init();
489}
490
491template<Assembly T>
492void init(T& container)
493{
494 for_each_component(container, [](auto& component) {init(component);});
495};
496
497template<Component T>
498void activate_inner(T& component)
499{
500 if constexpr (requires {component.main(component.inputs, component.outputs);})
501 component.main(component.inputs, component.outputs);
502 else if constexpr (requires {component(component.inputs, component.outputs);})
503 component(component.inputs, component.outputs);
504 else if constexpr (requires {component();})
505 component();
506
507}
508
509template<Component T>
510void activate(T& component)
511{
512 clear_output_flags(component);
513 activate_inner(component);
514 clear_input_flags(component);
515}
516
517template<Assembly T>
518void activate(T& container)
519{
520 clear_output_flags(container);
521 for_each_component(container, activate_inner);
522 clear_input_flags(container);
523};
524
527
528}
Definition sygac-components.hpp:161
Check if T is a Sygaldry component.
Definition sygac-components.hpp:124
Ensure T is not a union and is either scalar or aggregate. Doesn't catch inheritance!
Definition sygac-components.hpp:41
Definition sygac-tuple.hpp:29
Definition sygac-components.hpp:96
Definition sygac-components.hpp:93
Definition sygac-components.hpp:90
Definition sygac-components.hpp:85
#define tag(TAG)
Helper struct for defining recognized tags. This is immediately undefed, so don't try to use it!
Definition sygah-endpoints.hpp:238
Definition sygac-components.hpp:306
Base metafunction case for most types; most types are not assemblies.
Definition sygac-components.hpp:138
Default case for external destinations subroutine reflection where the subroutine does not exist.
Definition sygac-components.hpp:77
Default case for external sources subroutine reflection where the subroutine does not exist.
Definition sygac-components.hpp:69
Definition sygac-functions.hpp:107
Default case for init subroutine reflection where the init subroutine does not exist.
Definition sygac-components.hpp:61
Default case for main subroutine reflection where the main subroutine does not exist.
Definition sygac-components.hpp:48
Definition sygac-components.hpp:165
Definition sygac-components.hpp:166
Definition sygac-components.hpp:172
Definition sygac-components.hpp:169
Definition sygac-components.hpp:170
Definition sygac-components.hpp:167
Definition sygac-components.hpp:173
Definition sygac-components.hpp:175
Definition sygac-components.hpp:189
Definition sygac-components.hpp:185
Definition sygac-components.hpp:181
Definition sygac-components.hpp:177
Definition sygac-components.hpp:183
Definition sygac-components.hpp:179
Definition sygac-components.hpp:171
Definition sygac-components.hpp:168
Definition sygac-components.hpp:290
Definition sygac-components.hpp:288
Definition sygac-components.hpp:197