ComponentHierarchy.hpp
1 #pragma once
2 #include <score/application/ApplicationComponents.hpp>
3 #include <score/model/ComponentSerialization.hpp>
4 #include <score/tools/IdentifierGeneration.hpp>
5 
6 #include <ossia/detail/algorithms.hpp>
7 
8 #include <nano_observer.hpp>
9 
10 #include <vector>
11 
12 namespace score
13 {
25 template <typename ParentComponent_T, typename ChildModel_T, typename ChildComponent_T>
27  : public ParentComponent_T
28  , public Nano::Observer
29 {
30 public:
32 
33  struct ChildPair
34  {
35  ChildPair(ChildModel_T* m, ChildComponent_T* c)
36  : model{m}
37  , component{c}
38  {
39  }
40  ChildModel_T* model{};
41  ChildComponent_T* component{};
42  };
43 
44  template <typename... Args>
45  ComponentHierarchyManager(Args&&... args)
46  : ParentComponent_T{std::forward<Args>(args)...}
47  {
48  init();
49  }
50 
51  template <typename... Args>
53  : ParentComponent_T{std::forward<Args>(args)...}
54  {
55  }
56 
57  void init()
58  {
59  auto& child_models = ParentComponent_T::template models<ChildModel_T>();
60  for(auto& child_model : child_models)
61  {
62  add(child_model);
63  }
64 
65  child_models.mutable_added.template connect<&hierarchy_t::add>(this);
66 
67  child_models.removing.template connect<&hierarchy_t::remove>(this);
68  }
69 
70  const auto& children() const { return m_children; }
71 
72 #if defined(SCORE_SERIALIZABLE_COMPONENTS)
73  void add(ChildModel_T& element)
74  {
75  add(element, typename score::is_component_serializable<ChildComponent_T>::type{});
76  }
77 
78  void add(ChildModel_T& element, score::serializable_tag)
79  {
80  // Since the component may be serializable, we first look if
81  // we can deserialize it.
82  auto comp = score::deserialize_component<ChildComponent_T>(
83  element.components(), [&](auto&& deserializer) {
84  ParentComponent_T::template load<ChildComponent_T>(deserializer, element);
85  });
86 
87  // Maybe we could not deserialize it
88  if(!comp)
89  {
90  comp = ParentComponent_T::template make<ChildComponent_T>(
91  getStrongId(element.components()), element);
92  }
93 
94  // We try to add it
95  if(comp)
96  {
97  element.components().add(comp);
98  m_children.emplace_back(ChildPair{&element, comp});
99  }
100  }
101  void add(ChildModel_T& model, score::not_serializable_tag)
102 #else
103  void add(ChildModel_T& model)
104 #endif
105  {
106  // The subclass should provide this function to construct
107  // the correct component relative to this process.
108  auto proc_comp = ParentComponent_T::make(getStrongId(model.components()), model);
109  if(proc_comp)
110  {
111  model.components().add(proc_comp);
112  m_children.emplace_back(ChildPair{&model, proc_comp});
113  }
114  }
115 
116  void remove(const ChildModel_T& model)
117  {
118  auto it
119  = ossia::find_if(m_children, [&](auto pair) { return pair.model == &model; });
120 
121  if(it != m_children.end())
122  {
123  cleanup(*it);
124  m_children.erase(it);
125  }
126  }
127 
128  void cleanup(const ChildPair& pair)
129  {
130  ParentComponent_T::removing(*pair.model, *pair.component);
131  pair.model->components().remove(*pair.component);
132  }
133 
134  void clear()
135  {
136  for(const auto& element : m_children)
137  {
138  cleanup(element);
139  }
140  m_children.clear();
141  }
142 
143  ~ComponentHierarchyManager() { clear(); }
144 
145 private:
146  std::vector<ChildPair> m_children; // todo map ? multi_index with both index
147  // of the component and of the process ?
148 };
149 
159 template <
160  typename ParentComponent_T, typename ChildModel_T, typename ChildComponent_T,
161  typename ChildComponentFactoryList_T, bool HasOwnership = true>
163  : public ParentComponent_T
164  , public Nano::Observer
165 {
166 public:
168 
169  struct ChildPair
170  {
171  ChildPair(ChildModel_T* m, ChildComponent_T* c)
172  : model{m}
173  , component{c}
174  {
175  }
176  ChildModel_T* model{};
177  ChildComponent_T* component{};
178  };
179 
180  template <typename... Args>
182  : ParentComponent_T{std::forward<Args>(args)...}
183  , m_componentFactory{
184  score::AppComponents().template interfaces<ChildComponentFactoryList_T>()}
185  {
186  init_hierarchy();
187  }
188 
189  template <typename... Args>
191  : ParentComponent_T{std::forward<Args>(args)...}
192  , m_componentFactory{
193  score::AppComponents().template interfaces<ChildComponentFactoryList_T>()}
194  {
195  }
196 
197  void init_hierarchy()
198  {
199  auto& child_models = ParentComponent_T::template models<ChildModel_T>();
200  for(auto& child_model : child_models)
201  {
202  add(child_model);
203  }
204 
205  child_models.mutable_added.template connect<&hierarchy_t::add>(this);
206 
207  child_models.removing.template connect<&hierarchy_t::remove>(this);
208  }
209  const auto& children() const { return m_children; }
210 
211  void add(ChildModel_T& element)
212  {
213 #if defined(SCORE_SERIALIZABLE_COMPONENTS)
214  add_impl(
215  element, typename score::is_component_serializable<ChildComponent_T>::type{});
216 #else
217  add_impl(element);
218 #endif
219  }
220  void remove(const ChildModel_T& model)
221  {
222  auto it
223  = ossia::find_if(m_children, [&](auto pair) { return pair.model == &model; });
224 
225  if(it != m_children.end())
226  {
227  do_cleanup(*it);
228  m_children.erase(it);
229  }
230  }
231 
232  void clear()
233  {
234  for(const auto& element : m_children)
235  {
236  do_cleanup(element);
237  }
238  m_children.clear();
239  }
240 
241  ~PolymorphicComponentHierarchyManager() { clear(); }
242 
243 private:
244 #if defined(SCORE_SERIALIZABLE_COMPONENTS)
245  // TODO remove these useless templates when MSVC grows some brains
246  template <typename TheChild>
247  void add_impl(TheChild& model, score::serializable_tag)
248  {
249  // Will return a factory for the given process if available
250  if(auto factory = m_componentFactory.factory(model))
251  {
252  // Since the component may be serializable, we first look if
253  // we can deserialize it.
254  ChildComponent_T* comp = score::deserialize_component<ChildComponent_T>(
255  model.components(), [&](auto&& deserializer) {
256  ParentComponent_T::template load<ChildComponent_T>(
257  deserializer, *factory, model);
258  });
259 
260  // Maybe we could not deserialize it
261  if(!comp)
262  {
263  comp = ParentComponent_T::template make<ChildComponent_T>(
264  getStrongId(model.components()), *factory, model);
265  }
266 
267  // We try to add it
268  if(comp)
269  {
270  model.components().add(comp);
271  m_children.emplace_back(ChildPair{&model, comp});
272  ParentComponent_T::added(*comp);
273  }
274  }
275  }
276 
277  template <typename TheChild>
278  void add_impl(TheChild& model, score::not_serializable_tag)
279 #else
280  template <typename TheChild>
281  void add_impl(TheChild& model)
282 #endif
283  {
284  // Will return a factory for the given process if available
285  if(auto factory = m_componentFactory.factory(model))
286  {
287  // The subclass should provide this function to construct
288  // the correct component relative to this process.
289  auto comp = ParentComponent_T::make(*factory, model);
290  if(comp)
291  {
292  model.components().push_back(comp);
293  m_children.emplace_back(ChildPair{&model, comp});
294  ParentComponent_T::added(*comp);
295  }
296  }
297  else
298  {
299  auto comp = ParentComponent_T::make(model);
300  if(comp)
301  {
302  model.components().push_back(comp);
303  m_children.emplace_back(ChildPair{&model, comp});
304  ParentComponent_T::added(*comp);
305  }
306  }
307  }
308 
309  void do_cleanup(const ChildPair& pair)
310  {
311  if constexpr(HasOwnership)
312  {
313  ParentComponent_T::removing(*pair.model, *pair.component);
314  pair.model->components().remove(*pair.component);
315  }
316  else
317  {
318  auto t = ParentComponent_T::removing(*pair.model, *pair.component);
319  pair.model->components().erase(*pair.component);
320  ParentComponent_T::removed(*pair.model, *pair.component, std::move(t));
321  }
322  }
323 
324  const ChildComponentFactoryList_T& m_componentFactory;
325 
326  std::vector<ChildPair> m_children; // todo map ? multi_index with both index
327  // of the component and of the process ?
328 };
329 
330 template <typename Component>
331 using ComponentHierarchy = ComponentHierarchyManager<
332  Component, typename Component::model_t, typename Component::component_t>;
333 
334 template <typename Component, bool HasOwnership = true>
335 using PolymorphicComponentHierarchy = PolymorphicComponentHierarchyManager<
336  Component, typename Component::model_t, typename Component::component_t,
337  typename Component::component_factory_list_t, HasOwnership>;
338 }
Manages simple hierarchies of components.
Definition: ComponentHierarchy.hpp:29
Manages polymorphic hierarchies of components.
Definition: ComponentHierarchy.hpp:165
Base toolkit upon which the software is built.
Definition: Application.cpp:90
Definition: ComponentHierarchy.hpp:34
Definition: ComponentHierarchy.hpp:170
Definition: lib/score/model/Component.hpp:16