SpECTRE Documentation Coverage Report
Current view: top level - Utilities - StaticCache.hpp Hit Total Coverage
Commit: 817e13c5144619b701c7cd870655d8dbf94ab8ce Lines: 4 19 21.1 %
Date: 2024-07-19 22:17:05
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // 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 "Utilities/ErrorHandling/Assert.hpp"
      11             : #include "Utilities/Gsl.hpp"
      12             : #include "Utilities/Requires.hpp"
      13             : #include "Utilities/TypeTraits/IsInteger.hpp"
      14             : 
      15             : /// \ingroup UtilitiesGroup
      16             : /// Range of integral values for StaticCache indices.  The `Start` is inclusive
      17             : /// and the `End` is exclusive.  The range must not be empty.
      18             : template <auto Start, auto End>
      19           1 : struct CacheRange {
      20             :   static_assert(std::is_same_v<decltype(Start), decltype(End)>);
      21             :   static_assert(Start < End, "CacheRange must include at least one value");
      22           0 :   constexpr static auto start = Start;
      23           0 :   constexpr static auto end = End;
      24           0 :   constexpr static auto size = end - start;
      25           0 :   using value_type = std::remove_cv_t<decltype(start)>;
      26             : };
      27             : 
      28             : /// \ingroup UtilitiesGroup
      29             : /// Possible enumeration values for the StaticCache. Only values specified here
      30             : /// are retrievable.
      31             : ///
      32             : /// \note The `EnumerationType` must be streamable.
      33             : template <typename EnumerationType, EnumerationType... Enums>
      34           1 : struct CacheEnumeration {
      35           0 :   constexpr static size_t size = sizeof...(Enums);
      36           0 :   using value_type = EnumerationType;
      37             : };
      38             : 
      39             : /// \ingroup UtilitiesGroup
      40             : /// A cache of objects intended to be stored in a static variable.
      41             : ///
      42             : /// Objects can be accessed via a combination of several `size_t` and `enum`
      43             : /// arguments. The range of each integral argument is specified via a template
      44             : /// parameter of type `CacheRange<start, end>`, giving the first and
      45             : /// one-past-last values for the range. Each `enum` argument is specified by a
      46             : /// template parameter of type `CacheEnumeration<EnumerationType, Members...>`
      47             : /// giving the enumeration type and an explicit set of every enum member to be
      48             : /// cached.
      49             : ///
      50             : /// \example
      51             : /// A cache with only numeric indices:
      52             : /// \snippet Test_StaticCache.cpp static_cache
      53             : ///
      54             : /// \example
      55             : /// A cache with enumeration indices:
      56             : /// \snippet Test_StaticCache.cpp static_cache_with_enum
      57             : ///
      58             : /// \example
      59             : /// A cache with mixed numeric and enumeration indices:
      60             : /// \snippet Test_StaticCache.cpp static_cache_with_enum_and_numeric
      61             : ///
      62             : /// \example
      63             : /// A cache with no arguments at all (caching only a single object)
      64             : /// \snippet Test_StaticCache.cpp static_cache_no_args
      65             : ///
      66             : /// \see make_static_cache
      67             : ///
      68             : /// \tparam T type held in the cache
      69             : /// \tparam Ranges ranges of valid indices
      70             : template <typename Generator, typename T, typename... Ranges>
      71           1 : class StaticCache {
      72             :  public:
      73             :   template <typename Gen>
      74             :   // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
      75           0 :   explicit StaticCache(Gen&& generator)
      76             :       : generator_{std::forward<Gen>(generator)} {}
      77             : 
      78             :   template <typename... Args>
      79           0 :   const T& operator()(const Args... parameters) const {
      80             :     static_assert(sizeof...(parameters) == sizeof...(Ranges),
      81             :                   "Number of arguments must match number of ranges.");
      82             :     return unwrap_cache(generate_tuple<Ranges>(parameters)...);
      83             :   }
      84             : 
      85             :  private:
      86             :   template <typename Range, typename T1,
      87             :             Requires<not std::is_enum<T1>::value> = nullptr>
      88           0 :   auto generate_tuple(const T1 parameter) const {
      89             :     static_assert(
      90             :         tt::is_integer_v<std::remove_cv_t<T1>>,
      91             :         "The parameter passed for a CacheRange must be an integer type.");
      92             :     return std::make_tuple(
      93             :         static_cast<typename Range::value_type>(parameter),
      94             :         std::integral_constant<typename Range::value_type, Range::start>{},
      95             :         std::make_integer_sequence<typename Range::value_type, Range::size>{});
      96             :   }
      97             : 
      98             :   template <typename Range, typename T1,
      99             :             Requires<std::is_enum<T1>::value> = nullptr>
     100           0 :   std::tuple<std::remove_cv_t<T1>, Range> generate_tuple(
     101             :       const T1 parameter) const {
     102             :     static_assert(
     103             :         std::is_same<typename Range::value_type, std::remove_cv_t<T1>>::value,
     104             :         "Mismatched enum parameter type and cached type.");
     105             :     return {parameter, Range{}};
     106             :   }
     107             : 
     108             :   template <typename... IntegralConstantValues>
     109           0 :   const T& unwrap_cache() const {
     110             :     static const T cached_object = generator_(IntegralConstantValues::value...);
     111             :     return cached_object;
     112             :   }
     113             : 
     114             :   template <typename... IntegralConstantValues, auto IndexOffset, auto... Is,
     115             :             typename... Args>
     116           0 :   const T& unwrap_cache(
     117             :       std::tuple<
     118             :           std::remove_cv_t<decltype(IndexOffset)>,
     119             :           std::integral_constant<std::remove_cv_t<decltype(IndexOffset)>,
     120             :                                  IndexOffset>,
     121             :           std::integer_sequence<std::remove_cv_t<decltype(IndexOffset)>, Is...>>
     122             :           parameter0,
     123             :       Args... parameters) const {
     124             :     if (UNLIKELY(IndexOffset > std::get<0>(parameter0) or
     125             :                  std::get<0>(parameter0) >=
     126             :                      IndexOffset +
     127             :                          static_cast<decltype(IndexOffset)>(sizeof...(Is)))) {
     128             :       ERROR("Index out of range: "
     129             :             << IndexOffset << " <= " << std::get<0>(parameter0) << " < "
     130             :             << IndexOffset + static_cast<decltype(IndexOffset)>(sizeof...(Is)));
     131             :     }
     132             :     // note that the act of assigning to the specified function pointer type
     133             :     // fixes the template arguments that need to be inferred.
     134             :     static const std::array<
     135             :         const T& (StaticCache<Generator, T, Ranges...>::*)(Args...) const,
     136             :         sizeof...(Is)>
     137             :         cache{{&StaticCache<Generator, T, Ranges...>::unwrap_cache<
     138             :             IntegralConstantValues...,
     139             :             std::integral_constant<decltype(IndexOffset),
     140             :                                    Is + IndexOffset>>...}};
     141             :     // The array `cache` holds pointers to member functions, so we dereference
     142             :     // the pointer and invoke it on `this`.
     143             : #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ > 10 && __GNUC__ < 14
     144             : #pragma GCC diagnostic push
     145             : #pragma GCC diagnostic ignored "-Warray-bounds"
     146             : #endif
     147             :     return (this->*gsl::at(cache, std::get<0>(parameter0) - IndexOffset))(
     148             :         parameters...);
     149             : #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ > 10 && __GNUC__ < 14
     150             : #pragma GCC diagnostic pop
     151             : #endif
     152             :   }
     153             : 
     154             :   template <typename... IntegralConstantValues, typename EnumType,
     155             :             EnumType... EnumValues, typename... Args>
     156           0 :   const T& unwrap_cache(
     157             :       std::tuple<EnumType, CacheEnumeration<EnumType, EnumValues...>>
     158             :           parameter0,
     159             :       Args... parameters) const {
     160             :     size_t array_location = std::numeric_limits<size_t>::max();
     161             :     static const std::array<EnumType, sizeof...(EnumValues)> values{
     162             :         {EnumValues...}};
     163             :     for (size_t i = 0; i < sizeof...(EnumValues); ++i) {
     164             :       if (std::get<0>(parameter0) == gsl::at(values, i)) {
     165             :         array_location = i;
     166             :         break;
     167             :       }
     168             :     }
     169             :     if (UNLIKELY(array_location == std::numeric_limits<size_t>::max())) {
     170             :       ERROR("Uncached enumeration value: " << std::get<0>(parameter0));
     171             :     }
     172             :     // note that the act of assigning to the specified function pointer type
     173             :     // fixes the template arguments that need to be inferred.
     174             :     static const std::array<
     175             :         const T& (StaticCache<Generator, T, Ranges...>::*)(Args...) const,
     176             :         sizeof...(EnumValues)>
     177             :         cache{{&StaticCache<Generator, T, Ranges...>::unwrap_cache<
     178             :             IntegralConstantValues...,
     179             :             std::integral_constant<EnumType, EnumValues>>...}};
     180             :     // The array `cache` holds pointers to member functions, so we dereference
     181             :     // the pointer and invoke it on `this`.
     182             : #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ > 10 && __GNUC__ < 14
     183             : #pragma GCC diagnostic push
     184             : #pragma GCC diagnostic ignored "-Warray-bounds"
     185             : #endif
     186             :     return (this->*gsl::at(cache, array_location))(parameters...);
     187             : #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ > 10 && __GNUC__ < 14
     188             : #pragma GCC diagnostic pop
     189             : #endif
     190             :   }
     191             : 
     192           0 :   const Generator generator_;
     193             : };
     194             : 
     195             : /// \ingroup UtilitiesGroup
     196             : /// Create a StaticCache, inferring the cached type from the generator.
     197             : template <typename... Ranges, typename Generator>
     198           1 : auto make_static_cache(Generator&& generator) {
     199             :   using CachedType = std::remove_cv_t<decltype(generator(
     200             :       std::declval<typename Ranges::value_type>()...))>;
     201             :   return StaticCache<std::remove_cv_t<Generator>, CachedType, Ranges...>(
     202             :       std::forward<Generator>(generator));
     203             : }

Generated by: LCOV version 1.14