OpenKalman
generate.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_GENERATE_HPP
17 #define OPENKALMAN_COLLECTIONS_VIEWS_GENERATE_HPP
18 
19 #include <type_traits>
21 #ifdef __cpp_lib_ranges
22 #include <ranges>
23 #else
25 #endif
26 #include "values/concepts/size.hpp"
29 
31 {
38 #ifdef __cpp_concepts
39  template<typename F, values::size Size = std::unreachable_sentinel_t> requires std::same_as<Size, std::remove_reference_t<Size>> and
40  std::invocable<F, std::size_t> and std::invocable<F, std::integral_constant<std::size_t, 0>>
41  struct generate_view : std::ranges::view_interface<generate_view<F, Size>>
42 #else
43  template<typename F, typename Size = unreachable_sentinel_t>
44  struct generate_view : ranges::view_interface<generate_view<F, Size>>
45 #endif
46  {
47  private:
48 
49  using Size_ = std::conditional_t<values::index<Size>, Size, std::monostate>;
50 
52 
53 #if __cplusplus >= 202002L
54  static constexpr bool static_F = std::semiregular<F_box>;
55 #else
56  static constexpr bool static_F = semiregular<F_box>;
57 #endif
58 
59 
60  template<bool Const, typename T>
61  using maybe_const = std::conditional_t<Const, const T, T>;
62 
63  public:
64 
68  template<bool Const>
69  struct iterator
70  {
71  private:
72 
73  using F_box_c = maybe_const<Const, F_box>;
74 
75  using F_ref = std::conditional_t<static_F, F_box, F_box_c *>;
76 
77  constexpr F_box&
78  get_f() const noexcept
79  {
80  if constexpr (static_F) return const_cast<F_box&>(f_ref);
81  else return const_cast<F_box&>(*f_ref);
82  }
83 
84  static constexpr F_ref
85  init_f_ref(F_box_c& f) noexcept
86  {
87  if constexpr (static_F) return const_cast<F_ref&&>(f);
88  else return std::addressof(f);
89  }
90 
91  public:
92 
93  using iterator_concept = std::random_access_iterator_tag;
94  using iterator_category = std::random_access_iterator_tag;
95  using value_type = std::invoke_result_t<F_box&, std::size_t>;
96  using difference_type = std::ptrdiff_t;
97  using reference = value_type;
98  using pointer = void;
99 
100  constexpr iterator() = default;
101 
102  constexpr iterator(F_box_c& f, std::size_t pos)
103  : f_ref {init_f_ref(f)}, current_ {static_cast<difference_type>(pos)} {};
104 
105  constexpr iterator(iterator<not Const> i) : f_ref {std::move(i.f_ref)}, current_ {std::move(i.current_)} {}
106 
107  constexpr value_type operator*() const
108  {
109  return get_f()(static_cast<std::size_t>(current_));
110  }
111 
112  constexpr value_type operator[](difference_type offset) const
113  {
114  return get_f()(static_cast<std::size_t>(current_ + offset));
115  }
116 
117  constexpr auto& operator++() noexcept { ++current_; return *this; }
118  constexpr auto operator++(int) noexcept { auto temp = *this; ++*this; return temp; }
119  constexpr auto& operator--() noexcept { --current_; return *this; }
120  constexpr auto operator--(int) noexcept { auto temp = *this; --*this; return temp; }
121  constexpr auto& operator+=(const difference_type diff) noexcept { current_ += diff; return *this; }
122  constexpr auto& operator-=(const difference_type diff) noexcept { current_ -= diff; return *this; }
123 
124  friend constexpr auto operator+(const iterator& it, const difference_type diff)
125  { return iterator {it.get_f(), static_cast<std::size_t>(it.current_ + diff)}; }
126  friend constexpr auto operator+(const difference_type diff, const iterator& it)
127  { return iterator {it.get_f(), static_cast<std::size_t>(diff + it.current_)}; }
128  friend constexpr auto operator-(const iterator& it, const difference_type diff)
129  { if (it.current_ < diff) throw std::out_of_range{"Iterator out of range"}; return iterator {it.get_f(), static_cast<std::size_t>(it.current_ - diff)}; }
130  friend constexpr difference_type operator-(const iterator& it, const iterator& other)
131  { return it.current_ - other.current_; }
132  friend constexpr bool operator==(const iterator& it, const iterator& other)
133  { return it.current_ == other.current_; }
134 #ifdef __cpp_impl_three_way_comparison
135  constexpr auto operator<=>(const iterator& other) const noexcept { return current_ <=> other.current_; }
136 #else
137  constexpr bool operator!=(const iterator& other) const noexcept { return current_ != other.current_; }
138  constexpr bool operator<(const iterator& other) const noexcept { return current_ < other.current_; }
139  constexpr bool operator>(const iterator& other) const noexcept { return current_ > other.current_; }
140  constexpr bool operator<=(const iterator& other) const noexcept { return current_ <= other.current_; }
141  constexpr bool operator>=(const iterator& other) const noexcept { return current_ >= other.current_; }
142 #endif
143 
144  private:
145 
146  F_ref f_ref;
147  difference_type current_;
148 
149  }; // struct Iterator
150 
151 
155 #ifdef __cpp_concepts
156  template<typename Arg> requires std::constructible_from<const F, Arg&&> and values::index<Size>
157  constexpr
158  generate_view(Arg&& arg, Size_ size) noexcept
159 #else
160  template<typename Arg, std::enable_if_t<std::is_constructible_v<const F, Arg&&> and values::index<Size>, int> = 0>
161  constexpr
162  generate_view(Arg&& arg, Size_ size) noexcept
163 #endif
164  : f_box {const_cast<F&&>(std::forward<Arg>(arg))}, size_ {std::move(size)} {}
165 
166 
170 #ifdef __cpp_concepts
171  constexpr
172  generate_view(const F&) noexcept requires static_F and (not values::index<Size>)
173 #else
174  template<bool Enable = true, std::enable_if_t<Enable and static_F and (not values::index<Size>), int> = 0>
175  constexpr
176  generate_view(const F&) noexcept
177 #endif
178  : f_box {F{}}, size_ {} {}
179 
180 
184 #ifdef __cpp_concepts
185  template<typename Arg> requires std::constructible_from<const F, Arg&&> and (not static_F) and (not values::index<Size>)
186  constexpr
187  generate_view(Arg&& arg) noexcept requires (not static_F)
188 #else
189  template<bool Enable = true, typename Arg, std::enable_if_t<Enable and
190  std::is_constructible_v<const F, Arg&&> and not static_F and (not values::index<Size>), int> = 0>
191  constexpr
192  generate_view(Arg&& arg) noexcept
193 #endif
194  : f_box {const_cast<F&&>(std::forward<Arg>(arg))}, size_ {} {}
195 
196 
200 #ifdef __cpp_concepts
201  explicit constexpr
202  generate_view(Size_ size) noexcept requires static_F and values::index<Size>
203 #else
204  template<bool Enable = true, std::enable_if_t<Enable and static_F and values::index<Size>, int> = 0>
205  explicit constexpr
206  generate_view(Size_ size) noexcept
207 #endif
208  : f_box {F{}}, size_ {std::move(size)} {}
209 
210 
214  constexpr
215  generate_view() = default;
216 
217 
221  constexpr auto begin() { return iterator<false> {f_box, 0}; }
222 
224  constexpr auto begin() const { return iterator<true> {f_box, 0}; }
225 
226 
230  constexpr auto end()
231  {
232  using namespace std;
233  if constexpr (values::index<Size>)
234  return iterator<false> {f_box, static_cast<std::size_t>(size_)};
235  else
236  return unreachable_sentinel;
237  }
238 
240  constexpr auto end() const
241  {
242  using namespace std;
243  if constexpr (values::index<Size>)
244  return iterator<true> {f_box, static_cast<std::size_t>(size_)};
245  else
246  return unreachable_sentinel;
247  }
248 
249 
253 #ifdef __cpp_concepts
254  constexpr auto
255  size() const noexcept requires values::index<Size>
256 #else
257  template<bool Enable = true, std::enable_if_t<Enable and (values::index<Size>), int> = 0>
258  constexpr auto size() const noexcept
259 #endif
260  {
261  return size_;
262  }
263 
264 
268 #ifdef __cpp_explicit_this_parameter
269  template<std::size_t i>
270  constexpr decltype(auto)
271  get(this auto&& self) noexcept
272  {
273  if constexpr (values::fixed<Size>) static_assert(i < values::fixed_number_of_v<Size>, "Index out of range");
274  return std::forward<decltype(self)>(self).f_box(std::integral_constant<std::size_t, i>{});
275  }
276 #else
277  template<std::size_t i>
278  constexpr decltype(auto)
279  get() &
280  {
281  using namespace std;
282  if constexpr (values::fixed<Size>) static_assert(i < values::fixed_number_of_v<Size>, "Index out of range");
283  return f_box(std::integral_constant<std::size_t, i>{});
284  }
285 
286  template<std::size_t i>
287  constexpr decltype(auto)
288  get() const &
289  {
290  using namespace std;
291  if constexpr (values::fixed<Size>) static_assert(i < values::fixed_number_of_v<Size>, "Index out of range");
292  return f_box(std::integral_constant<std::size_t, i>{});
293  }
294 
295  template<std::size_t i>
296  constexpr decltype(auto)
297  get() && noexcept
298  {
299  using namespace std;
300  if constexpr (values::fixed<Size>) static_assert(i < values::fixed_number_of_v<Size>, "Index out of range");
301  return std::move(*this).f_box(std::integral_constant<std::size_t, i>{});
302  }
303 
304  template<std::size_t i>
305  constexpr decltype(auto)
306  get() const && noexcept
307  {
308  using namespace std;
309  if constexpr (values::fixed<Size>) static_assert(i < values::fixed_number_of_v<Size>, "Index out of range");
310  return std::move(*this).f_box(std::integral_constant<std::size_t, i>{});
311  }
312 #endif
313 
314  private:
315 
316  F_box f_box;
317  Size_ size_;
318 
319  };
320 
321 
325  template<typename F, typename S>
326  generate_view(F&&, const S&) -> generate_view<F, S>;
327 
329  template<typename F>
331 
332 
333 } // namespace OpenKalman::values
334 
335 
336 #ifdef __cpp_lib_ranges
337 namespace std::ranges
338 #else
339 namespace OpenKalman::ranges
340 #endif
341 {
342  template<typename F, typename S>
343  constexpr bool enable_borrowed_range<OpenKalman::collections::generate_view<F, S>> = std::is_lvalue_reference_v<F> or
344 #if __cplusplus >= 202002L
345  std::semiregular<OpenKalman::collections::internal::movable_wrapper<F>>;
346 #else
347  semiregular<OpenKalman::collections::internal::movable_wrapper<F>>;
348 #endif
349 }
350 
351 
352 namespace std
353 {
354  template<typename F, typename S>
356 
357 
358  template<std::size_t i, typename F, typename S>
359  struct tuple_element<i, OpenKalman::collections::generate_view<F, S>>
360  {
361  using type = std::invoke_result_t<F, std::integral_constant<std::size_t, i>>;
362  };
363 
364 } // namespace std
365 
366 
368 {
369  namespace detail
370  {
372  {
376 #ifdef __cpp_concepts
377  template<typename F, values::index Size> requires
378  std::invocable<F, std::size_t> and std::invocable<F, std::integral_constant<std::size_t, 0>>
379 #else
380  template<typename F, typename Size, std::enable_if_t<values::index<Size> and
381  std::is_invocable_v<F, std::size_t> and std::is_invocable_v<F, std::integral_constant<std::size_t, 0>>, int> = 0>
382 #endif
383  constexpr auto
384  operator() (F&& f, Size s) const
385  {
386  return generate_view {std::forward<F>(f), std::move(s)};
387  }
388 
389 
393 #ifdef __cpp_concepts
394  template<typename F> requires
395  std::invocable<F, std::size_t> and std::invocable<F, std::integral_constant<std::size_t, 0>>
396 #else
397  template<typename F, std::enable_if_t<std::is_invocable_v<F, std::size_t> and
398  std::is_invocable_v<F, std::integral_constant<std::size_t, 0>>, int> = 0>
399 #endif
400  constexpr auto
401  operator() (F&& f) const
402  {
403  return generate_view {std::forward<F>(f)};
404  }
405 
406  };
407 
408  }
409 
410 
417 
418 }
419 
420 #endif //OPENKALMAN_COLLECTIONS_VIEWS_GENERATE_HPP
Namespace for collections.
Definition: collections.hpp:27
generate_view(F &&, const S &) -> generate_view< F, S >
Deduction guide.
constexpr auto size() const noexcept
The size of the resulting object.
Definition: generate.hpp:258
Definition for values::size.
Iterator for generate_view.
Definition: generate.hpp:69
Definition: view_interface.hpp:32
constexpr generate_view(const F &) noexcept
Construct from a statically constructable callable object, if the view is unsized.
Definition: generate.hpp:176
constexpr generate_view(Arg &&arg) noexcept
Construct from a callable object, if the view is unsized.
Definition: generate.hpp:192
Definition: tuple_reverse.hpp:103
constexpr auto begin() const
Definition: generate.hpp:224
constexpr auto begin()
Definition: generate.hpp:221
constexpr generate_view(Size_ size) noexcept
Construct from a size if the function can be defined statically.
Definition: generate.hpp:206
A collection_view created by lazily generating elements based on an index.
Definition: generate.hpp:44
The root namespace for OpenKalman.
Definition: basics.hpp:34
Definitions relating to the availability of c++ language features.
The fixed number associated with a values::fixed.
Definition: fixed_number_of.hpp:45
decltype(auto) constexpr get() &
Get element i.
Definition: generate.hpp:279
Namespace for generalized views.
Definition: collections.hpp:33
constexpr detail::generate_adaptor generate
a collection_view generator associated with generate_view.
Definition: generate.hpp:416
Definitions implementing features of the c++ ranges library for compatibility.
constexpr generate_view()=default
Default constructor.
constexpr generate_view(Arg &&arg, Size_ size) noexcept
Construct from a callable object and a size.
Definition: generate.hpp:162
constexpr auto end()
Definition: generate.hpp:230
constexpr auto end() const
Definition: generate.hpp:240