OpenKalman
to_tuple.hpp
Go to the documentation of this file.
1 /* This file is part of OpenKalman, a header-only C++ library for
2  * Kalman filters and other recursive filters.
3  *
4  * Copyright (c) 2025 Christopher Lee Ogden <ogden@gatech.edu>
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9  */
10 
16 #ifndef OPENKALMAN_COLLECTIONS_VIEWS_TO_TUPLE_HPP
17 #define OPENKALMAN_COLLECTIONS_VIEWS_TO_TUPLE_HPP
18 
19 #include <type_traits>
20 #ifdef __cpp_lib_ranges
21 #include <ranges>
22 #else
26 #endif
33 
35 {
36  namespace detail_to_tuple
37  {
38  using namespace std;
39 #ifdef __cpp_lib_ranges
40  namespace ranges = std::ranges;
41 #endif
42 
43 
44 #ifdef __cpp_concepts
45  template<std::size_t i, typename T>
46 #else
47  template<std::size_t i, typename T, typename = void>
48 #endif
50  {
51  using type = ranges::range_value_t<T>;
52 
53  template<typename R> constexpr decltype(auto) operator() (R&& r) const
54  { return collections::get(std::forward<R>(r), std::integral_constant<std::size_t, i>{}); }
55  };
56 
57 
58 #ifdef __cpp_concepts
59  template<std::size_t i, typename T> requires gettable<i, T>
60  struct tuple_element_impl<i, T>
61 #else
62  template<std::size_t i, typename T>
63  struct tuple_element_impl<i, T, std::enable_if_t<gettable<i, T>>>
64 #endif
65  : std::tuple_element<i, T>
66  {
67  using type = std::tuple_element_t<i, T>;
68 
69  template<typename R> constexpr decltype(auto) operator() (R&& r) const
70  { return OpenKalman::internal::generalized_std_get<i>(std::forward<R>(r)); }
71  };
72 
73 
74 #ifdef __cpp_concepts
75  template<std::size_t i, typename T>
76 #else
77  template<std::size_t i, typename T, typename = void>
78 #endif
79  struct get_elem : tuple_element_impl<i, T> {};
80 
81 
82  template<std::size_t i, typename R>
83  inline constexpr decltype(auto)
84  get_from_base(R&& r) { using namespace std; return get_elem<i, remove_cvref_t<R>>{}(std::forward<R>(r)); }
85 
86 
87  template<typename T>
88  struct get_elem<0, ranges::single_view<T>>
89  {
90  using type = T;
91  template<typename U> constexpr decltype(auto) operator() (U&& u) const { return *std::forward<U>(u).data(); }
92  };
93 
94 
95  template<std::size_t i, typename R>
96  struct get_elem<i, ranges::ref_view<R>> : get_elem<i, remove_cvref_t<R>>
97  {
98  template<typename T> constexpr decltype(auto) operator() (T&& t) const { return get_from_base<i>(std::forward<T>(t).base()); }
99  };
100 
101 
102  template<std::size_t i, typename R>
103  struct get_elem<i, ranges::owning_view<R>> : get_elem<i, remove_cvref_t<R>>
104  {
105  template<typename T> constexpr decltype(auto) operator() (T&& t) const { return get_from_base<i>(std::forward<T>(t).base()); }
106  };
107 
108 
109 #ifdef __cpp_concepts
110  template<std::size_t i, typename V> requires (size_of_v<V> != dynamic_size)
111  struct get_elem<i, ranges::reverse_view<V>>
112 #else
113  template<std::size_t i, typename V>
114  struct get_elem<i, ranges::reverse_view<V>, std::enable_if_t<size_of_v<V> != dynamic_size>>
115 #endif
116  : get_elem<size_of_v<V> - i - 1_uz, remove_cvref_t<V>>
117  {
118  template<typename T> constexpr decltype(auto) operator() (T&& t) const
119  {
120  if constexpr (not std::is_lvalue_reference_v<T> or std::is_copy_constructible_v<V>)
121  return get_from_base<size_of_v<V> - i - 1_uz>(std::forward<T>(t).base());
122  else
123  return collections::get(std::forward<T>(t), std::integral_constant<std::size_t, i>{});
124  }
125  };
126 
127  }
128 
129 
134 #ifdef __cpp_lib_ranges
135  template<std::ranges::random_access_range V> requires std::ranges::view<std::remove_cvref_t<V>> or std::ranges::viewable_range<V>
136  struct to_tuple : std::ranges::view_interface<to_tuple<V>>
137 #else
138  template<typename V>
139  struct to_tuple : ranges::view_interface<to_tuple<V>>
140 #endif
141  {
142  private:
143 
145 
146  public:
147 
151 #ifdef __cpp_concepts
152  constexpr
153  to_tuple() = default;
154 #else
155  template<bool Enable = true, std::enable_if_t<Enable and std::is_default_constructible_v<RangeBox>, int> = 0>
156  constexpr
157  to_tuple() {}
158 #endif
159 
160 
164 #if defined(__cpp_concepts) and defined(__cpp_lib_remove_cvref)
165  template<typename Arg> requires std::constructible_from<RangeBox, Arg&&> and (not std::same_as<std::remove_cvref_t<Arg>, to_tuple>)
166 #else
167  template<typename Arg, std::enable_if_t<
168  std::is_constructible_v<RangeBox, Arg&&> and (not std::is_same_v<remove_cvref_t<Arg>, to_tuple>), int> = 0>
169 #endif
170  constexpr explicit
171  to_tuple(Arg&& arg) noexcept : r_ {std::forward<Arg>(arg)} {}
172 
173 
177 #ifdef __cpp_explicit_this_parameter
178  constexpr decltype(auto)
179  base(this auto&& self) noexcept { return std::forward<decltype(self)>(self).r_.get(); }
180 #else
181  constexpr V& base() & { return this->r_.get(); }
182  constexpr const V& base() const & { return this->r_.get(); }
183  constexpr V&& base() && noexcept { return std::move(*this).r_.get(); }
184  constexpr const V&& base() const && noexcept { return std::move(*this).r_.get(); }
185 #endif
186 
187 
191  constexpr auto begin()
192  {
193 #ifdef __cpp_lib_ranges
194  namespace ranges = std::ranges;
195 #endif
196  return ranges::begin(base());
197  }
198 
200  constexpr auto begin() const
201  {
202 #ifdef __cpp_lib_ranges
203  namespace ranges = std::ranges;
204 #endif
205  return ranges::begin(base());
206  }
207 
208 
212  constexpr auto end()
213  {
214 #ifdef __cpp_lib_ranges
215  namespace ranges = std::ranges;
216 #endif
217  return ranges::end(base());
218  }
219 
221  constexpr auto end() const
222  {
223 #ifdef __cpp_lib_ranges
224  namespace ranges = std::ranges;
225 #endif
226  return ranges::end(base());
227  }
228 
229 
233 #ifdef __cpp_concepts
234  constexpr auto
235  size() const requires sized<V>
236 #else
237  template<bool Enable = true, std::enable_if_t<Enable and sized<V>, int> = 0>
238  constexpr auto
239  size() const
240 #endif
241  {
242  return get_size(base());
243  }
244 
245 
249 #ifdef __cpp_concepts
250  constexpr auto
251  empty() const requires sized<V> or std::ranges::forward_range<V>
252 #else
253  template<bool Enable = true, std::enable_if_t<Enable and (sized<V> or ranges::forward_range<V>), int> = 0>
254  constexpr auto
255  empty() const
256 #endif
257  {
258  if constexpr (sized<V>)
259  {
260  if constexpr (size_of_v<V> == dynamic_size) return get_size(base()) == 0_uz;
261  else if constexpr (size_of_v<V> == 0) return std::true_type{};
262  else return std::false_type{};
263  }
264  else
265  {
266 #ifdef __cpp_lib_ranges
267  namespace ranges = std::ranges;
268 #endif
269  return ranges::begin(base()) == ranges::end(base());
270  }
271  }
272 
273 
277  constexpr decltype(auto)
278  front() { return collections::get(base(), std::integral_constant<std::size_t, 0>{}); }
279 
281  constexpr decltype(auto)
282  front() const { return collections::get(base(), std::integral_constant<std::size_t, 0>{}); }
283 
284  private:
285 
286 #ifdef __cpp_lib_ranges
287  template<typename T>
288 #else
289  template<typename T, typename = void>
290 #endif
291  struct back_gettable : std::false_type {};
292 
293 #ifdef __cpp_lib_ranges
294  template<sized T>
295  struct back_gettable<T>
296 #else
297  template<typename T>
298  struct back_gettable<T, std::enable_if_t<sized<T>>>
299 #endif
300  : std::bool_constant<size_of_v<T> != dynamic_size and gettable<size_of_v<T> - 1_uz, T>> {};
301 
302  public:
303 
307 #ifdef __cpp_lib_ranges
308  constexpr decltype(auto)
309  back() requires (std::ranges::bidirectional_range<V> and std::ranges::common_range<V>) or back_gettable<V>::value
310 #else
311  template<bool Enable = true, std::enable_if_t<Enable and
312  (ranges::bidirectional_range<V> and ranges::common_range<V>) or back_gettable<V>::value, int> = 0>
313  constexpr decltype(auto)
314  back()
315 #endif
316  {
317  if constexpr (back_gettable<V>::value)
318  { return OpenKalman::internal::generalized_std_get<size_of_v<V> - 1_uz>(base()); }
319  else
320  {
321 #ifdef __cpp_lib_ranges
322  namespace ranges = std::ranges;
323 #endif
324  using namespace std; return ranges::view_interface<to_tuple<V>>::back();
325  }
326  }
327 
329 #ifdef __cpp_lib_ranges
330  constexpr decltype(auto)
331  back() const requires (std::ranges::bidirectional_range<V> and std::ranges::common_range<V>) or back_gettable<V>::value
332 #else
333  template<bool Enable = true, std::enable_if_t<Enable and
334  (ranges::bidirectional_range<V> and ranges::common_range<V>) or back_gettable<V>::value, int> = 0>
335  constexpr decltype(auto)
336  back() const
337 #endif
338  {
339  if constexpr (back_gettable<V>::value)
340  { return OpenKalman::internal::generalized_std_get<size_of_v<V> - 1_uz>(base()); }
341  else
342  {
343 #ifdef __cpp_lib_ranges
344  namespace ranges = std::ranges;
345 #endif
346  using namespace std; return ranges::view_interface<to_tuple<V>>::back();
347  }
348  }
349 
350 
354 #ifdef __cpp_explicit_this_parameter
355  template<typename Self, values::index I>
356  constexpr decltype(auto)
357  operator[](this Self&& self, I i) noexcept
358  {
359  if constexpr (values::fixed<I> and sized<V>)
360  static_assert(size_of_v<V> == dynamic_size or values::fixed_number_of_v<I> < size_of_v<V>, "Index out of range");
361  return collections::get(std::forward<Self>(self), std::move(i));
362  }
363 #else
364  template<typename I, std::enable_if_t<values::index<I>, int> = 0>
365  constexpr decltype(auto)
366  operator[](I i) &
367  {
368  if constexpr (values::fixed<I> and sized<V>)
369  static_assert(size_of_v<V> == dynamic_size or values::fixed_number_of_v<I> < size_of_v<V>, "Index out of range");
370  return collections::get(*this, std::move(i));
371  }
372 
373  template<typename I, std::enable_if_t<values::index<I>, int> = 0>
374  constexpr decltype(auto)
375  operator[](I i) const &
376  {
377  if constexpr (values::fixed<I> and sized<V>)
378  static_assert(size_of_v<V> == dynamic_size or values::fixed_number_of_v<I> < size_of_v<V>, "Index out of range");
379  return collections::get(*this, std::move(i));
380  }
381 
382  template<typename I, std::enable_if_t<values::index<I>, int> = 0>
383  constexpr decltype(auto)
384  operator[](I i) && noexcept
385  {
386  if constexpr (values::fixed<I> and sized<V>)
387  static_assert(size_of_v<V> == dynamic_size or values::fixed_number_of_v<I> < size_of_v<V>, "Index out of range");
388  return collections::get(std::move(*this), std::move(i));
389  }
390 
391  template<typename I, std::enable_if_t<values::index<I>, int> = 0>
392  constexpr decltype(auto)
393  operator[](I i) const && noexcept
394  {
395  if constexpr (values::fixed<I> and sized<V>)
396  static_assert(size_of_v<V> == dynamic_size or values::fixed_number_of_v<I> < size_of_v<V>, "Index out of range");
397  return collections::get(std::move(*this), std::move(i));
398  }
399 #endif
400 
401 
405 #ifdef __cpp_explicit_this_parameter
406  template<std::size_t i>
407  constexpr decltype(auto)
408  get(this auto&& self) noexcept
409  {
410  if constexpr(sized<V>) if constexpr (size_of_v<V> != dynamic_size) static_assert(i < size_of_v<V>, "Index out of range");
411  return detail_to_tuple::get_from_base<i>(std::forward<decltype(self)>(self).base());
412  }
413 #else
414  template<std::size_t i>
415  constexpr decltype(auto)
416  get() &
417  {
418  if constexpr(sized<V>) if constexpr (size_of_v<V> != dynamic_size) static_assert(i < size_of_v<V>, "Index out of range");
419  return detail_to_tuple::get_from_base<i>(base());
420  }
421 
422  template<std::size_t i>
423  constexpr decltype(auto)
424  get() const &
425  {
426  if constexpr(sized<V>) if constexpr (size_of_v<V> != dynamic_size) static_assert(i < size_of_v<V>, "Index out of range");
427  return detail_to_tuple::get_from_base<i>(base());
428  }
429 
430  template<std::size_t i>
431  constexpr decltype(auto)
432  get() && noexcept
433  {
434  if constexpr(sized<V>) if constexpr (size_of_v<V> != dynamic_size) static_assert(i < size_of_v<V>, "Index out of range");
435  return detail_to_tuple::get_from_base<i>(std::move(*this).base());
436  }
437 
438  template<std::size_t i>
439  constexpr decltype(auto)
440  get() const && noexcept
441  {
442  if constexpr(sized<V>) if constexpr (size_of_v<V> != dynamic_size) static_assert(i < size_of_v<V>, "Index out of range");
443  return detail_to_tuple::get_from_base<i>(std::move(*this).base());
444  }
445 #endif
446 
447  private:
448 
449  RangeBox r_;
450 
451  };
452 
453 
454  template<typename V>
455  to_tuple(V&&) -> to_tuple<V>;
456 
457 
458 #ifndef __cpp_concepts
459  namespace detail_to_tuple
460  {
461  template<typename V, typename = void>
462  struct tuple_size {};
463 
464  template<typename V>
465  struct tuple_size<V, std::enable_if_t<sized<V> and size_of<V>::value != dynamic_size>> : size_of<V> {};
466  }
467 #endif
468 
469 } // namespace OpenKalman::collections
470 
471 
472 #ifdef __cpp_lib_ranges
473 namespace std::ranges
474 #else
475 namespace OpenKalman::ranges
476 #endif
477 {
478  template<typename V>
479  constexpr bool enable_borrowed_range<OpenKalman::collections::to_tuple<V>> =
480  std::is_lvalue_reference_v<V> or enable_borrowed_range<remove_cvref_t<V>>;
481 }
482 
483 
484 namespace std
485 {
486 #ifdef __cpp_concepts
487  template<OpenKalman::collections::sized V> requires (OpenKalman::collections::size_of_v<V> != OpenKalman::dynamic_size)
489 #else
490  template<typename V>
491  struct tuple_size<OpenKalman::collections::to_tuple<V>> : OpenKalman::collections::detail_to_tuple::tuple_size<V> {};
492 #endif
493 
494 
495  template<std::size_t i, typename V>
496  struct tuple_element<i, OpenKalman::collections::to_tuple<V>>
497 #ifdef __cpp_lib_remove_cvref
498  : OpenKalman::collections::detail_to_tuple::get_elem<i, remove_cvref_t<V>> {};
499 #else
501 #endif
502 }
503 
504 
505 #endif //OPENKALMAN_COLLECTIONS_VIEWS_TO_TUPLE_HPP
Definition for values::index.
Namespace for collections.
Definition: collections.hpp:27
Definition for collections::get.
decltype(auto) constexpr get(Arg &&arg, I i)
A generalization of std::get.
Definition: get.hpp:62
Definition: view_interface.hpp:32
A collection_view created from a std::ranges::random_access_range that is a std::ranges::viewable_ran...
Definition: to_tuple.hpp:139
Definition: tuple_reverse.hpp:103
The size of a sized object (including a collection).
Definition: size_of.hpp:36
constexpr auto empty() const
Definition: to_tuple.hpp:255
constexpr bool value
T is numerical value or is reducible to a numerical value.
Definition: value.hpp:31
constexpr auto end()
An iterator to the end of the range.
Definition: to_tuple.hpp:212
Definition for collections::get_size.
The root namespace for OpenKalman.
Definition: basics.hpp:34
Definitions relating to the availability of c++ language features.
constexpr to_tuple(Arg &&arg) noexcept
Construct from a std::ranges::random_access_range.
Definition: to_tuple.hpp:171
constexpr bool size
T is either an index representing a size, or void which represents that there is no size...
Definition: size.hpp:32
constexpr auto begin() const
Definition: to_tuple.hpp:200
constexpr to_tuple()
Default constructor.
Definition: to_tuple.hpp:157
constexpr V & base() &
The base tuple.
Definition: to_tuple.hpp:181
Definitions implementing features of the c++ ranges library for compatibility.
Definition for ::fixed.
constexpr std::size_t dynamic_size
A constant indicating that a size or index is dynamic.
Definition: global-definitions.hpp:33
constexpr auto size() const
The size of the resulting object.
Definition: to_tuple.hpp:239
constexpr auto get_size(Arg &&arg)
Get the size of a sized object (e.g, a collection)
Definition: get_size.hpp:191
constexpr auto begin()
An iterator to the beginning of the range.
Definition: to_tuple.hpp:191
constexpr auto end() const
Definition: to_tuple.hpp:221