StaticCache.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <array>
7 #include <cstddef>
8 #include <utility>
9 
10 #include "ErrorHandling/Assert.hpp"
11 #include "Utilities/Gsl.hpp"
12 #include "Utilities/Requires.hpp"
13 #include "Utilities/TypeTraits.hpp"
14 
15 namespace StaticCache_detail {
16 template <typename T, typename... Ranges>
17 class StaticCacheImpl;
18 
19 template <typename T, typename Range0, typename... Ranges>
20 class StaticCacheImpl<T, Range0, Ranges...> {
21  template <typename>
22  struct construction_helper;
23 
24  template <size_t... RangeValues>
25  struct construction_helper<std::index_sequence<RangeValues...>> {
26  template <typename Generator, typename... Args>
27  constexpr static std::array<StaticCacheImpl<T, Ranges...>, Range0::size>
28  construct(Generator&& generator, const Args... args) noexcept {
29  return {{StaticCacheImpl<T, Ranges...>(generator, args...,
30  RangeValues + Range0::start)...}};
31  }
32  };
33 
34  public:
35  template <typename Generator, typename... Args,
37  StaticCacheImpl>> = nullptr>
38  explicit constexpr StaticCacheImpl(Generator&& generator,
39  const Args... args) noexcept
40  : data_(construction_helper<
41  std::make_index_sequence<Range0::size>>::construct(generator,
42  args...)) {}
43 
44  template <typename... Args>
45  const T& operator()(const size_t first_index, const Args... rest) const
46  noexcept {
47  ASSERT(Range0::start <= first_index and first_index < Range0::end,
48  "Index out of range: " << Range0::start << " <= " << first_index
49  << " < " << Range0::end);
50  return gsl::at(data_, first_index - Range0::start)(rest...);
51  }
52 
53  private:
54  std::array<StaticCacheImpl<T, Ranges...>, Range0::size> data_;
55 };
56 
57 template <typename T>
58 class StaticCacheImpl<T> {
59  public:
60  template <typename Generator, typename... Args,
61  Requires<not cpp17::is_same_v<std::decay_t<Generator>,
62  StaticCacheImpl>> = nullptr>
63  explicit constexpr StaticCacheImpl(Generator&& generator,
64  const Args... args) noexcept
65  : data_(generator(args...)) {}
66 
67  const T& operator()() const noexcept { return data_; }
68 
69  private:
70  T data_;
71 };
72 } // namespace StaticCache_detail
73 
74 /// \ingroup UtilitiesGroup
75 /// A cache of objects intended to be stored in a static variable.
76 ///
77 /// Objects can be accessed using several `size_t` arguments in the
78 /// ranges specified as template parameters. The CacheRange template
79 /// parameters give first and one-past-last values for the valid
80 /// ranges for each argument.
81 ///
82 /// \example
83 /// \snippet Test_StaticCache.cpp static_cache
84 ///
85 /// \see make_static_cache
86 ///
87 /// \tparam T type held in the cache
88 /// \tparam Ranges ranges of valid indices
89 template <typename T, typename... Ranges>
90 class StaticCache {
91  public:
92  /// Initialize the cache. All objects will be created by calling
93  /// `generator` before the constructor returns.
94  // clang-tidy: misc-forwarding-reference-overload - fixed with
95  // Requires, but clang-tidy can't recognize that.
96  template <typename Generator,
97  Requires<not cpp17::is_same_v<std::decay_t<Generator>,
98  StaticCache>> = nullptr>
99  explicit constexpr StaticCache(Generator&& generator) noexcept // NOLINT
100  : data_(generator) {}
101 
102  template <typename... Args>
103  const T& operator()(const Args... indices) const noexcept {
104  static_assert(sizeof...(Args) == sizeof...(Ranges),
105  "Number of arguments must match number of ranges.");
106  return data_(static_cast<size_t>(indices)...);
107  }
108 
109  private:
110  StaticCache_detail::StaticCacheImpl<T, Ranges...> data_;
111 };
112 
113 /// \ingroup UtilitiesGroup
114 /// Create a StaticCache, inferring the cached type from the generator.
115 template <typename... Ranges, typename Generator>
116 auto make_static_cache(Generator&& generator) noexcept {
117  using CachedType = std::decay_t<decltype(generator((Ranges{}, size_t{})...))>;
118  return StaticCache<CachedType, Ranges...>(generator);
119 }
120 
121 /// \ingroup UtilitiesGroup
122 /// Range of values for StaticCache indices. The `Start` is inclusive
123 /// and the `End` is exclusive. The range must not be empty.
124 template <size_t Start, size_t End>
125 struct CacheRange {
126  static_assert(Start < End, "CacheRange must include at least one value");
127  constexpr static size_t start = Start;
128  constexpr static size_t end = End;
129  constexpr static size_t size = end - start;
130 };
A cache of objects intended to be stored in a static variable.
Definition: StaticCache.hpp:90
Range of values for StaticCache indices. The Start is inclusive and the End is exclusive. The range must not be empty.
Definition: StaticCache.hpp:125
constexpr StaticCache(Generator &&generator) noexcept
Initialize the cache. All objects will be created by calling generator before the constructor returns...
Definition: StaticCache.hpp:99
#define ASSERT(a, m)
Assert that an expression should be true.
Definition: Assert.hpp:49
Defines the type alias Requires.
auto make_static_cache(Generator &&generator) noexcept
Create a StaticCache, inferring the cached type from the generator.
Definition: StaticCache.hpp:116
Defines macro ASSERT.
Defines functions and classes from the GSL.
typename Requires_detail::requires_impl< B >::template_error_type_failed_to_meet_requirements_on_template_parameters Requires
Express requirements on the template parameters of a function or class, replaces std::enable_if_t ...
Definition: Requires.hpp:67
Defines type traits, some of which are future STL type_traits header.
constexpr T & at(std::array< T, N > &arr, Size index)
Retrieve a entry from a container, with checks in Debug mode that the index being retrieved is valid...
Definition: Gsl.hpp:124
Definition: StaticCache.hpp:15