OpenKalman
concat.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 
17 #ifndef OPENKALMAN_COMPATIBILITY_VIEWS_CONCAT_HPP
18 #define OPENKALMAN_COMPATIBILITY_VIEWS_CONCAT_HPP
19 
20 #if __cpp_lib_ranges_concat < 202403L
21 
22 #ifdef __cpp_lib_ranges
23 #include <ranges>
24 #else
25 #include "view-concepts.hpp"
26 #include "view_interface.hpp"
27 #include "all.hpp"
28 #endif
29 
30 namespace OpenKalman::ranges
31 {
37 #ifdef __cpp_lib_ranges
38  template<std::ranges::random_access_range...Views> requires (... and std::ranges::view<Views>) and (sizeof...(Views) > 0)
39  struct concat_view : std::ranges::view_interface<concat_view<Views...>>
40 #else
41  template<typename...Views>
42  struct concat_view : ranges::view_interface<concat_view<Views...>>
43 #endif
44  {
45  private:
46 
47 #if __cplusplus >= 202002L
48  template<typename...Rs> using concat_reference_t = std::common_reference_t<std::ranges::range_reference_t<Rs>...>;
49  template<typename...Rs> using concat_value_t = std::common_type_t<std::ranges::range_value_t<Rs>...>;
50  template<typename...Rs> using concat_rvalue_reference_t = std::common_reference_t<std::ranges::range_rvalue_reference_t<Rs>...>;
51 #else
52  template<typename...Rs> using concat_reference_t = common_reference_t<ranges::range_reference_t<Rs>...>;
53  template<typename...Rs> using concat_value_t = std::common_type_t<ranges::range_value_t<Rs>...>;
54  template<typename...Rs> using concat_rvalue_reference_t = common_reference_t<ranges::range_rvalue_reference_t<Rs>...>;
55 #endif
56 
57  template<bool Const, typename Rs>
58  using maybe_const = std::conditional_t<Const, const Rs, Rs>;
59 
60  using ViewsTup = std::tuple<Views...>;
61 
62 
63  class concat_index_table
64  {
65  template<std::size_t...cs>
66  #ifdef __cpp_lib_constexpr_vector
67  static constexpr auto
68  #else
69  static auto
70  #endif
71  make_index_table_range(const ViewsTup& tup, std::index_sequence<cs...>)
72  {
73  std::vector<std::size_t> table;
74  auto it = std::back_inserter(table);
75 #ifdef __cpp_lib_ranges
76  (std::fill_n(it, std::ranges::size(std::get<cs>(tup)), cs), ...);
77 #else
78  (std::fill_n(it, ranges::size(std::get<cs>(tup)), cs), ...);
79 #endif
80  return table;
81  }
82 
83 
84  static constexpr auto
85  make_index_table(const ViewsTup& tup)
86  {
87  return make_index_table_range(tup, std::index_sequence_for<Views...>{});
88  }
89 
90  public:
91 
92  explicit constexpr concat_index_table(const ViewsTup& rs_tup) : value {make_index_table(rs_tup)} {}
93 
94  decltype(make_index_table(std::declval<const ViewsTup&>())) value;
95 
96  };
97 
98 
99  class concat_start_table
100  {
101  template<std::size_t i = 0, typename...Locs>
102  static constexpr auto
103  make_start_table(const ViewsTup& tup, std::size_t currloc = 0_uz, Locs...locs)
104  {
105  if constexpr (i < sizeof...(Views))
106  {
107 #ifdef __cpp_lib_ranges
108  std::size_t next_loc = currloc + std::ranges::size(std::get<i>(tup));
109 #else
110  std::size_t next_loc = currloc + ranges::size(std::get<i>(tup));
111 #endif
112  return make_start_table<i + 1>(tup, next_loc, locs..., currloc);
113  }
114  else
115  {
116  return std::array {locs...};
117  }
118  }
119 
120  public:
121 
122  explicit constexpr concat_start_table(const ViewsTup& rs_tup) : value {make_start_table(rs_tup)} {}
123 
124  decltype(make_start_table(std::declval<const ViewsTup&>())) value;
125 
126  };
127 
128 
129  using IndexTable = decltype(concat_index_table(std::declval<ViewsTup>()).value);
130 
131  using StartTable = decltype(concat_start_table(std::declval<ViewsTup>()).value);
132 
133  public:
134 
135  template<bool Const>
136  struct iterator
137  {
138  using iterator_concept = std::random_access_iterator_tag;
139  using iterator_category = iterator_concept;
140  using value_type = concat_value_t<maybe_const<Const, Views>...>;
141 #if __cplusplus >= 202002L
142  using difference_type = std::common_type_t<std::ranges::range_difference_t<maybe_const<Const, Views>>...>;
143 #else
144  using difference_type = std::common_type_t<ranges::range_difference_t<maybe_const<Const, Views>>...>;
145 #endif
146  using pointer = void;
147  using reference = concat_reference_t<maybe_const<Const, Views>...>;
148 
149  private:
150 
151  using Tup = maybe_const<Const, ViewsTup>;
152 
153  template<typename = std::index_sequence_for<Views...>>
154  struct tuple_concat_iterator_call_table;
155 
156  template<std::size_t...ix>
157  struct tuple_concat_iterator_call_table<std::index_sequence<ix...>>
158  {
159  template<std::size_t i>
160  static constexpr value_type
161  call_table_get(Tup& tup, std::size_t local_i) noexcept
162  {
163  return std::get<i>(tup)[local_i];
164  }
165 
166  static constexpr std::array value {call_table_get<ix>...};
167  };
168 
169  using table = tuple_concat_iterator_call_table<>;
170 
171  public:
172 
173 #ifdef __cpp_lib_ranges
174  constexpr iterator() = default;
175 
176  constexpr iterator(iterator<not Const> it) requires Const and
177  (... and std::convertible_to<std::ranges::iterator_t<Views>, std::ranges::iterator_t<const Views>>)
178  : parent(it.parent), current(it.current) {}
179 
180  //template<typename...Args>
181  constexpr explicit iterator(maybe_const<Const, concat_view>* parent, difference_type p)
182  //requires std::constructible_from<base_iter, Args&&...>
183  : parent(parent), current(p) {}
184 #else
185  constexpr iterator() : parent{nullptr} {};
186 
187  template<bool C = Const, std::enable_if_t<C and
188  (... and std::is_convertible_v<ranges::iterator_t<Views>, ranges::iterator_t<const Views>>), int> = 0>
189  constexpr iterator(iterator<not C> it) : parent(it.parent), current(it.current) {}
190 
191  //template<typename...Args, bool Enable = true, std::enable_if_t<Enable and std::is_constructible_v<base_iter, Args&&...>, int> = 0>
192  constexpr explicit iterator(maybe_const<Const, concat_view>* parent, difference_type p) : parent(parent), current(p) {}
193 #endif
194 
195  constexpr iterator(const iterator& other) = default;
196  constexpr iterator(iterator&& other) noexcept = default;
197  constexpr iterator& operator=(const iterator& other) = default;
198  constexpr iterator& operator=(iterator&& other) noexcept = default;
199  explicit constexpr operator std::size_t() const noexcept { return static_cast<std::size_t>(current); }
200 
201  constexpr decltype(auto) operator*() noexcept
202  {
203  std::size_t c = (parent->index_table)[static_cast<std::size_t>(current)];
204  std::size_t local_i = current - parent->start_table[c];
205  return table::value[c](parent->views_tup, local_i);
206  }
207  constexpr decltype(auto) operator*() const noexcept
208  {
209  std::size_t c = (parent->index_table)[static_cast<std::size_t>(current)];
210  std::size_t local_i = current - parent->start_table[c];
211  return table::value[c](parent->views_tup, local_i);
212  }
213  constexpr decltype(auto) operator[](difference_type offset) noexcept
214  {
215  auto i = static_cast<std::size_t>(current + offset);
216  std::size_t c = parent->index_table[i];
217  std::size_t local_i = i - parent->start_table[c];
218  return table::value[c](parent->views_tup, local_i);
219  }
220  constexpr decltype(auto) operator[](difference_type offset) const noexcept
221  {
222  auto i = static_cast<std::size_t>(current + offset);
223  std::size_t c = parent->index_table[i];
224  std::size_t local_i = i - parent->start_table[c];
225  return table::value[c](parent->views_tup, local_i);
226  }
227  constexpr auto& operator++() noexcept { ++current; return *this; }
228  constexpr auto operator++(int) noexcept { auto temp = *this; ++*this; return temp; }
229  constexpr auto& operator--() noexcept { --current; return *this; }
230  constexpr auto operator--(int) noexcept { auto temp = *this; --*this; return temp; }
231  constexpr auto& operator+=(const difference_type diff) noexcept { current += diff; return *this; }
232  constexpr auto& operator-=(const difference_type diff) noexcept { current -= diff; return *this; }
233 
234  friend constexpr auto operator+(const iterator& it, const difference_type diff) noexcept
235  {
236  return iterator {it.parent, it.current + diff};
237  }
238  friend constexpr auto operator+(const difference_type diff, const iterator& it) noexcept
239  {
240  return iterator {it.parent, diff + it.current};
241  }
242  friend constexpr auto operator-(const iterator& it, const difference_type diff) noexcept
243  {
244  return iterator {it.parent, it.current - diff};
245  }
246  friend constexpr difference_type operator-(const iterator& it, const iterator& other) noexcept
247  {
248  return it.current - other.current;
249  }
250  friend constexpr bool operator==(const iterator& it, const iterator& other) noexcept
251  {
252  return it.current == other.current;
253  }
254 #ifdef __cpp_impl_three_way_comparison
255  constexpr auto operator<=>(const iterator& other) const noexcept { return current <=> other.current; }
256 #else
257  constexpr bool operator!=(const iterator& other) const noexcept { return current != other.current; }
258  constexpr bool operator<(const iterator& other) const noexcept { return current < other.current; }
259  constexpr bool operator>(const iterator& other) const noexcept { return current > other.current; }
260  constexpr bool operator<=(const iterator& other) const noexcept { return current <= other.current; }
261  constexpr bool operator>=(const iterator& other) const noexcept { return current >= other.current; }
262 #endif
263 
264  private:
265 
266  maybe_const<Const, concat_view>* parent;
267  difference_type current;
268 
269  };
270 
271 
272 #ifdef __cpp_concepts
273  constexpr concat_view() = default;
274 #else
275  template<typename aT = void, std::enable_if_t<std::is_void_v<aT> and (... and std::is_default_constructible_v<Views>), int> = 0>
276  constexpr concat_view() {}
277 #endif
278 
279 
280  explicit constexpr concat_view(Views...vs) : views_tup {std::move(vs)...} {}
281 
282 
283  constexpr auto
284  begin() noexcept
285  {
286  return iterator<false> {this, 0};
287  }
288 
289 
290  constexpr auto
291  begin() const noexcept
292  {
293  return iterator<true> {this, 0};
294  }
295 
296 
297  constexpr auto
298  end() noexcept
299  {
300  return iterator<false> {this, static_cast<std::ptrdiff_t>(size())};
301  }
302 
303 
304  constexpr auto
305  end() const noexcept
306  {
307  return iterator<true> {this, static_cast<std::ptrdiff_t>(size())};
308  }
309 
310  private:
311 
312  template<typename F, typename Tuple>
313  static constexpr auto tuple_transform(F&& f, Tuple&& tuple)
314  {
315  return std::apply([&f](auto&&...args)
316  {
317  return std::tuple<std::invoke_result_t<F&, decltype(args)>...>(std::invoke(f, std::forward<decltype(args)>(args))...);
318  }, std::forward<Tuple>(tuple));
319 
320  }
321 
322  public:
323 
324 #ifdef __cpp_lib_ranges
325  constexpr auto size() const requires (... and std::ranges::sized_range<Views>)
326 #else
327  template<typename aT = void, std::enable_if_t<std::is_void_v<aT> and (... and ranges::sized_range<Views>), int> = 0>
328  constexpr auto size() const
329 #endif
330  {
331  return std::apply([](auto...sizes) { return (... + static_cast<std::size_t>(sizes)); },
332 #ifdef __cpp_lib_ranges
333  tuple_transform(std::ranges::size, views_tup));
334 #else
335  tuple_transform(ranges::size, views_tup));
336 #endif
337  }
338 
339  private:
340 
341  ViewsTup views_tup;
342 
343  IndexTable index_table = concat_index_table(views_tup).value;
344 
345  StartTable start_table = concat_start_table(views_tup).value;
346 
347  };
348 
349 
350  template<typename...Rs>
351 #ifdef __cpp_lib_ranges
353 #else
355 #endif
356 
357 }
358 
359 
361 {
362  namespace detail
363  {
365  {
366 #ifdef __cpp_lib_ranges
367  template<std::ranges::viewable_range...Rs> requires (... and std::ranges::random_access_range<Rs>)
368 #else
369  template<typename...Rs, std::enable_if_t<(... and (ranges::viewable_range<Rs> and ranges::random_access_range<Rs>)), int> = 0>
370 #endif
371  constexpr auto
372  operator() (Rs&&...rs) const
373  {
374 #ifdef __cpp_lib_ranges
375  namespace ns = std;
376 #else
377  namespace ns = OpenKalman;
378 #endif
379  if constexpr (sizeof...(Rs) == 1 and (... and ns::ranges::input_range<Rs>))
380  return ns::ranges::views::all(std::forward<Rs>(rs)...);
381  else
382  return concat_view {ns::ranges::views::all(std::forward<Rs>(rs))...};
383  }
384  };
385 
386  }
387 
388 
393  inline constexpr detail::concat_adaptor concat;
394 
395 }
396 
397 #endif
398 
399 #endif //OPENKALMAN_COMPATIBILITY_VIEWS_CONCAT_HPP
constexpr detail::concat_adaptor concat
a std::ranges::range_adaptor_closure for a set of concatenated collection objects.
Definition: concat.hpp:173
Definition: view_interface.hpp:32
Definition: tuple_reverse.hpp:103
constexpr bool value
T is numerical value or is reducible to a numerical value.
Definition: value.hpp:31
Definition: concat.hpp:42
The root namespace for OpenKalman.
Definition: basics.hpp:34
Definition: range-access.hpp:25
constexpr bool size
T is either an index representing a size, or void which represents that there is no size...
Definition: size.hpp:32
Definition: all.hpp:35