Low-Level Abstraction of Memory Access
Accessors.hpp
Go to the documentation of this file.
1 // Copyright 2023 Bernhard Manfred Gruber
2 // SPDX-License-Identifier: MPL-2.0
3 
4 #pragma once
5 
6 #include "Concepts.hpp"
7 #include "ProxyRefOpMixin.hpp"
8 #include "macros.hpp"
9 
10 #include <atomic>
11 #include <memory>
12 #include <mutex>
13 
14 namespace llama::accessor
15 {
18  struct Default
19  {
20  template<typename Reference>
21  LLAMA_FN_HOST_ACC_INLINE auto operator()(Reference&& r) const -> Reference
22  {
23  return std::forward<Reference>(r);
24  }
25  };
26 
29  struct ByValue
30  {
31  template<typename Reference>
32  LLAMA_FN_HOST_ACC_INLINE auto operator()(Reference&& r) const
33  {
34  using ValueType = std::decay_t<Reference>;
35  if constexpr(isProxyReference<ValueType>)
36  return static_cast<typename ValueType::value_type>(r);
37  else
38  return ValueType{r};
39  }
40  };
41 
44  struct Const
45  {
46  // for l-value references
47  template<typename T>
48  LLAMA_FN_HOST_ACC_INLINE auto operator()(T& r) const -> const T&
49  {
50  return r;
51  }
52 
53  template<typename Ref>
54  // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
55  struct Reference : ProxyRefOpMixin<Reference<Ref>, typename Ref::value_type>
56  {
57  private:
58  Ref ref;
59 
60  public:
61  using value_type = typename Ref::value_type;
62 
63  LLAMA_FN_HOST_ACC_INLINE constexpr explicit Reference(Ref ref) : ref{ref}
64  {
65  }
66 
67  Reference(const Reference&) = default;
68 
69  // NOLINTNEXTLINE(bugprone-unhandled-self-assignment,cert-oop54-cpp)
70  LLAMA_FN_HOST_ACC_INLINE constexpr auto operator=(const Reference& other) -> Reference&
71  {
72  *this = static_cast<value_type>(other);
73  return *this;
74  }
75 
76  // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
78  {
79  return static_cast<value_type>(ref);
80  }
81 
82  template<typename T>
84  {
85  static_assert(sizeof(T) == 0, "You cannot write through a Const accessor");
86  return *this;
87  }
88  };
89 
90  // for proxy references
91  template<typename ProxyReference, std::enable_if_t<llama::isProxyReference<ProxyReference>, int> = 0>
92  LLAMA_FN_HOST_ACC_INLINE auto operator()(ProxyReference r) const
93  {
94  return Reference<ProxyReference>{std::move(r)};
95  }
96  };
97 
100  struct Restrict
101  {
102  template<typename T>
103  LLAMA_FN_HOST_ACC_INLINE auto operator()(T& r) const -> T& __restrict
104  {
105  return r;
106  }
107  };
108 
109 #ifdef __cpp_lib_atomic_ref
112  struct Atomic
113  {
114  template<typename T>
115  LLAMA_FORCE_INLINE auto operator()(T& r) const -> std::atomic_ref<T>
116  {
117  return std::atomic_ref<T>{r};
118  }
119  };
120 #endif
121 
124  template<typename Mutex = std::mutex>
125  struct Locked
126  {
127  // mutexes are usually not movable, so we put them on the heap, so the accessor is movable
128  std::unique_ptr<Mutex> m = std::make_unique<Mutex>();
129 
130  template<typename Ref, typename Value>
131  // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
132  struct Reference : ProxyRefOpMixin<Reference<Ref, Value>, Value>
133  {
134  Ref ref;
135  Mutex& m;
136 
137  using value_type = Value;
138 
139  // NOLINTNEXTLINE(bugprone-unhandled-self-assignment,cert-oop54-cpp)
140  LLAMA_FORCE_INLINE constexpr auto operator=(const Reference& other) -> Reference&
141  {
142  const std::lock_guard<Mutex> lock(m);
143  *this = static_cast<value_type>(other);
144  return *this;
145  }
146 
147  // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
149  {
150  const std::lock_guard<Mutex> lock(m);
151  return static_cast<value_type>(ref);
152  }
153 
154  template<typename T>
156  {
157  const std::lock_guard<Mutex> lock(m);
158  ref = t;
159  return *this;
160  }
161  };
162 
163  template<typename PR>
165  {
166  return {{}, r, *m};
167  }
168 
169  template<typename T>
171  {
172  return {{}, r, *m};
173  }
174  };
175 
176  namespace internal
177  {
178  template<std::size_t I, typename Accessor>
179  struct StackedLeave : Accessor
180  {
181  };
182  } // namespace internal
183 
187  template<typename... Accessors>
188  struct Stacked : internal::StackedLeave<0, Default>
189  {
190  };
191 
193  template<typename FirstAccessor, typename... MoreAccessors>
194  struct Stacked<FirstAccessor, MoreAccessors...>
195  : internal::StackedLeave<1 + sizeof...(MoreAccessors), FirstAccessor>
196  , Stacked<MoreAccessors...>
197  {
198  using First = internal::StackedLeave<1 + sizeof...(MoreAccessors), FirstAccessor>;
199  using Rest = Stacked<MoreAccessors...>;
200 
202 
203  LLAMA_FN_HOST_ACC_INLINE explicit Stacked(FirstAccessor first, MoreAccessors... rest)
204  : First{std::move(first)}
205  , Rest{std::move(rest)...}
206  {
207  }
208 
209  template<typename Reference>
210  LLAMA_FN_HOST_ACC_INLINE auto operator()(Reference&& r) const -> decltype(auto)
211  {
212  return static_cast<const Rest&>(*this)(static_cast<const First&>(*this)(std::forward<Reference>(r)));
213  }
214  };
215 } // namespace llama::accessor
#define LLAMA_EXPORT
Definition: macros.hpp:192
#define LLAMA_FORCE_INLINE
Forces the compiler to inline a function annotated with this macro.
Definition: macros.hpp:45
#define LLAMA_FN_HOST_ACC_INLINE
Definition: macros.hpp:96
CRTP mixin for proxy reference types to support all compound assignment and increment/decrement opera...
Accessor wrapping a reference into a std::atomic_ref. Can only wrap l-value references.
Definition: Accessors.hpp:113
auto operator()(T &r) const -> std::atomic_ref< T >
Definition: Accessors.hpp:115
Allows only read access and returns values instead of references to memory.
Definition: Accessors.hpp:30
auto operator()(Reference &&r) const
Definition: Accessors.hpp:32
constexpr Reference(Ref ref)
Definition: Accessors.hpp:63
Reference(const Reference &)=default
auto operator=(T) -> Reference &
Definition: Accessors.hpp:83
typename Ref::value_type value_type
Definition: Accessors.hpp:61
constexpr auto operator=(const Reference &other) -> Reference &
Definition: Accessors.hpp:70
Allows only read access by qualifying the references to memory with const.
Definition: Accessors.hpp:45
auto operator()(ProxyReference r) const
Definition: Accessors.hpp:92
auto operator()(T &r) const -> const T &
Definition: Accessors.hpp:48
Default accessor. Passes through the given reference.
Definition: Accessors.hpp:19
auto operator()(Reference &&r) const -> Reference
Definition: Accessors.hpp:21
constexpr auto operator=(const Reference &other) -> Reference &
Definition: Accessors.hpp:140
auto operator=(T t) -> Reference &
Definition: Accessors.hpp:155
Locks a mutex during each access to the data structure.
Definition: Accessors.hpp:126
std::unique_ptr< Mutex > m
Definition: Accessors.hpp:128
auto operator()(PR r) const -> Reference< PR, typename PR::value_type >
Definition: Accessors.hpp:164
auto operator()(T &r) const -> Reference< T &, std::remove_cv_t< T >>
Definition: Accessors.hpp:170
Qualifies references to memory with __restrict. Only works on l-value references.
Definition: Accessors.hpp:101
auto operator()(T &r) const -> T &__restrict
Definition: Accessors.hpp:103
Stacked(FirstAccessor first, MoreAccessors... rest)
Definition: Accessors.hpp:203
auto operator()(Reference &&r) const -> decltype(auto)
Definition: Accessors.hpp:210