Lumiera  0.pre.03
»edit your freedom«
hetero-data.hpp
Go to the documentation of this file.
1 /*
2  HETERO-DATA.hpp - handle chain of heterogeneous data blocks
3 
4  Copyright (C)
5  2024, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 */
13 
14 
67 #ifndef LIB_HETERO_DATA_H
68 #define LIB_HETERO_DATA_H
69 
70 
71 #include "lib/error.hpp"
72 #include "lib/nocopy.hpp"
73 #include "lib/meta/typelist.hpp"
77 
78 #include <utility>
79 #include <tuple>
80 
81 
82 namespace lib {
83 
91  template<typename...DATA>
92  class HeteroData;
93 
95  struct StorageLoc
97  {
98  StorageLoc* next{nullptr};
99  };
100 
102  template<size_t seg, typename...DATA>
104  : protected StorageLoc
105  , std::tuple<DATA...>
106  {
107  using Tuple = std::tuple<DATA...>;
108 
109  using Tuple::tuple;
110 
111  template<typename SPEC>
112  void linkInto (HeteroData<SPEC>&);
113 
114  template<size_t slot> auto& get() noexcept { return std::get<slot>(*this); }
115  template<typename X> auto& get() noexcept { return std::get<X>(*this); }
116  };
117 
118 
125  template<size_t seg, typename...DATA, class TAIL>
126  class HeteroData<meta::Node<StorageFrame<seg, DATA...>,TAIL>>
127  : StorageFrame<seg, DATA...>
128  {
129  using _Self = HeteroData;
130  using _Tail = HeteroData<TAIL>;
131  using Tuple = std::tuple<DATA...>;
132  using Frame = StorageFrame<seg, DATA...>;
133 
134  static constexpr size_t localSiz = sizeof...(DATA);
135 
136  template<size_t slot>
137  static constexpr bool isLocal = slot < localSiz;
138 
139  template<size_t slot>
140  using PickType = std::conditional_t<isLocal<slot>, std::tuple_element<slot,Tuple>
141  , typename _Tail::template PickType<slot-localSiz>>;
142  // need to use this helper to prevent eager evaluation on Elm_t<i>
143  _Tail&
144  accessTail()
145  {
146  REQUIRE (Frame::next, "HeteroData storage logic broken: follow-up extent not yet allocated");
147  return * reinterpret_cast<_Tail*> (Frame::next);
148  }
149 
150  template<typename...SPEC>
151  static _Self&
152  recast (HeteroData<SPEC...>& frontChain)
153  {
154  return reinterpret_cast<_Self&> (frontChain);
155  }
156  template<typename...SPEC>
157  static _Self const&
158  recast (HeteroData<SPEC...> const& frontChain)
159  {
160  return reinterpret_cast<_Self const&> (frontChain);
161  }
162 
163 
164  template<typename...XX>
165  friend class HeteroData;
166 
167  using Frame::Frame;
168 
169 
170  public:
171  HeteroData() = default;
172 
173  static constexpr size_t
174  size()
175  {
176  return localSiz + _Tail::size();
177  }
178 
180  template<size_t slot>
181  using Elm_t = typename PickType<slot>::type;
182 
183 
185  template<size_t slot>
186  Elm_t<slot>&
187  get() noexcept
188  {
189  static_assert (slot < size(), "HeteroData access index beyond defined data");
190  if constexpr (slot < localSiz)
191  return std::get<slot> (*this);
192  else
193  return accessTail().template get<slot-localSiz>();
194  }
195 
196  template<size_t slot>
197  Elm_t<slot> const&
198  get() const noexcept
199  {
200  return const_cast<HeteroData*>(this)->get<slot>();
201  }
202 
203 
210  template<size_t slot>
211  struct Accessor
212  {
213  using Type = Elm_t<slot>;
214 
215  template<class SPEC>
216  static Type&
217  get (HeteroData<SPEC>& frontEnd)
218  {
219  auto& fullChain = _Self::recast (frontEnd);
220  return fullChain.template get<slot>();
221  }
222 
223  template<typename HH>
224  Type& operator() (HH& frontEnd) const { return Accessor::get(frontEnd); }
225  };
226 
242  template<typename...VALS>
243  struct Chain
244  {
245  using Segments = meta::Node<Frame,TAIL>; // ◁———this type describes current chain structure
248  // ...and this would be the extended chain structure
249  template<typename...INIT>
250  static NewFrame
251  build (INIT&& ...initArgs)
252  {
253  return {initArgs ...}; // Note: NewFrame is non-copyable
254  }
255 
256  template<class HET>
257  static auto&
258  recast (HET& frontChain)
259  {
260  return ChainType::recast (frontChain);
261  }
262 
263  template<typename...XVALS>
264  using ChainExtent = typename ChainType::template Chain<XVALS...>;
265 
266  template<size_t slot>
267  using Accessor = typename ChainType::template Accessor<_Self::size()+slot>;
268 
269  template<typename X>
270  using AccessorFor = Accessor<meta::indexOfType<X,VALS...>()>;
271  };
272  };
273 
277  template<>
278  class HeteroData<meta::NullType>
279  {
280  public:
281  static size_t constexpr size() { return 0; }
282 
283  template<size_t>
284  using Elm_t = void;
285  template<size_t>
286  using PickType = void;
287  };
288 
289  /*************************************************************************/
295  template<typename...DATA>
296  class HeteroData
297  : public HeteroData<meta::Node<StorageFrame<0, DATA...>, meta::NullType>>
298  {
299  using _Front = HeteroData<meta::Node<StorageFrame<0, DATA...>, meta::NullType>>;
300 
301  public:
302  using NewFrame = typename _Front::Frame;
303  using ChainType = _Front;
304 
305  template<typename...INIT>
306  static _Front
307  build (INIT&& ...initArgs)
308  {
309  return {initArgs ...};
310  }
311  };
312 
313 
314  namespace {
328  inline StorageLoc*&
329  checkedTraversal (size_t segments, StorageLoc* last)
330  {
331  REQUIRE(last);
332  while (segments and last->next)
333  {
334  last = last->next;
335  --segments;
336  }
337  ASSERT (last->next == nullptr and segments == 1
338  ,"Failure to attach new data segment to HeteroData: "
339  "assumed type structure does not match real connectivity, "
340  "end-of-chain encountered with %d type segment(s) remaining"
341  , segments);
342  return last->next;
343  }
344  }//(End)helper
345 
346 
362  template<size_t seg, typename...DATA>
363  template<typename SPEC>
364  inline void
366  {
367  StorageLoc* firstSeg = reinterpret_cast<StorageLoc*> (&prefixChain);
368  StorageLoc*& lastLink = checkedTraversal (seg, firstSeg);
369  ENSURE (lastLink == nullptr);
370  lastLink = this;
371  }
372 }// namespace lib
373 
374 
375 
376 
377 namespace std { // Specialisation to support C++ »Tuple Protocol« and structured bindings.
378 
380  template<typename...DATA>
381  struct tuple_size<lib::HeteroData<DATA...> >
382  : std::integral_constant<std::size_t, lib::HeteroData<DATA...>::size()>
383  { };
384 
386  template<size_t I, typename...DATA>
387  struct tuple_element<I, lib::HeteroData<DATA...> >
388  {
389  using type = typename lib::HeteroData<DATA...>::template Elm_t<I>;
390  };
391  template<size_t I>
392  struct tuple_element<I, lib::HeteroData<lib::meta::NullType> >
393  {
394  static_assert ("accessing element-type of an empty HeteroData block");
395  };
396 
397  // Note: deliberately NOT providing a free get<i> function.
398  // Overload resolution would fail, since it attempts to instantiate std::get<i>(tuple) as a candidate,
399  // which triggers an assertion failure when using an index valid only for the full chain, not the base tuple
400 
401 
403  template<size_t seg, typename...DATA>
404  struct tuple_size<lib::StorageFrame<seg,DATA...> >
405  : std::tuple_size<typename lib::StorageFrame<seg,DATA...>::Tuple>
406  { };
407 
409  template<size_t I, size_t seg, typename...DATA>
410  struct tuple_element<I, lib::StorageFrame<seg,DATA...> >
411  : std::tuple_element<I, typename lib::StorageFrame<seg,DATA...>::Tuple>
412  { };
413 
414  // no need to define an overload for std::get<i>
415  // (other than a template specialisation, it will use base-type conversion to std::tuple on its argument;
416 
417 }// namespace std
418 #endif /*LIB_HETERO_DATA_H*/
A template metaprogramming technique for manipulating collections of types.
linked list of StorageFrame elements
Definition: hetero-data.hpp:95
Any copy and copy construction prohibited.
Definition: nocopy.hpp:37
STL namespace.
Metaprogramming: simple helpers for working with lists-of-types.
Some basic facilities for manipulating type sequences.
individual storage frame in a chain, holding a data tuple
Implementation namespace for support and library code.
typename PickType< slot >::type Elm_t
access type to reside in the given slot of the complete chain
StorageLoc *& checkedTraversal(size_t segments, StorageLoc *last)
Mix-Ins to allow or prohibit various degrees of copying and cloning.
A setup with chained data tuples residing in distributed storage.
Definition: hetero-data.hpp:92
void linkInto(HeteroData< SPEC > &)
Attach a new storage frame at the end of an existing HeteroData-chain.
Lumiera error handling (C++ interface).
Metaprogramming: Helpers for manipulating lists-of-types.