Low-Level Abstraction of Memory Access
Concepts.hpp
Go to the documentation of this file.
1 // Copyright 2022 Bernhard Manfred Gruber
2 // SPDX-License-Identifier: MPL-2.0
3 
4 #pragma once
5 
6 #include "Array.hpp"
7 #include "Core.hpp"
8 #include "RecordCoord.hpp"
9 
10 #include <type_traits>
11 
12 #if __has_include(<concepts>)
13 # include <concepts>
14 #endif
15 namespace llama
16 {
17 #ifdef __cpp_lib_concepts
19  template<auto I>
20  concept isConstexpr = requires { std::integral_constant<decltype(I), I>{}; };
21 
23  template<typename M>
24  concept Mapping = requires(M m) {
25  typename M::ArrayExtents;
26  typename M::RecordDim;
27  {
28  m.extents()
29  } -> std::same_as<typename M::ArrayExtents>;
30  {
31  +M::blobCount
32  } -> std::same_as<std::size_t>;
33  requires isConstexpr<M::blobCount>;
34  {
35  m.blobSize(typename M::ArrayExtents::value_type{})
36  } -> std::same_as<typename M::ArrayExtents::value_type>;
37  };
38 
40  template<typename M, typename RC>
41  concept PhysicalField = requires(M m, typename M::ArrayExtents::Index ai) {
42  {
43  m.blobNrAndOffset(ai, RC{})
44  } -> std::same_as<NrAndOffset<typename M::ArrayExtents::value_type>>;
45  };
46 
47  template<typename M>
48  struct MakeIsPhysical
49  {
50  template<typename RC>
51  using fn = mp_bool<PhysicalField<M, RC>>;
52  };
53 
55  template<typename M>
56  inline constexpr bool allFieldsArePhysical
57  = mp_all_of<LeafRecordCoords<typename M::RecordDim>, MakeIsPhysical<M>::template fn>::value;
58 
60  template<typename M>
61  concept PhysicalMapping = Mapping<M> && allFieldsArePhysical<M>;
62 
64  template<typename R>
65  concept LValueReference = std::is_lvalue_reference_v<R>;
66 
68  template<typename R>
69  concept AdlTwoStepSwappable = requires(R a, R b) { swap(a, b); } || requires(R a, R b) { std::swap(a, b); };
70 
72  template<typename R>
73  concept ProxyReference = std::is_copy_constructible_v<R> && std::is_copy_assignable_v<R> && requires(R r) {
74  typename R::value_type;
75  {
76  static_cast<typename R::value_type>(r)
77  } -> std::same_as<typename R::value_type>;
78  {
79  r = std::declval<typename R::value_type>()
80  } -> std::same_as<R&>;
81  } && AdlTwoStepSwappable<R>;
82 
84  template<typename R>
85  concept AnyReference = LValueReference<R> || ProxyReference<R>;
86 
88  template<typename R, typename T>
89  concept AnyReferenceTo = (LValueReference<R> && std::is_same_v<std::remove_cvref_t<R>, T>)
90  || (ProxyReference<R> && std::is_same_v<typename R::value_type, T>);
91 
93  template<typename M, typename RC>
94  concept ComputedField
95  = M::isComputed(RC{}) && requires(M m, typename M::ArrayExtents::Index ai, std::byte** blobs) {
96  {
97  m.compute(ai, RC{}, blobs)
98  } -> AnyReferenceTo<GetType<typename M::RecordDim, RC>>;
99  };
100 
101  template<typename M>
102  struct MakeIsComputed
103  {
104  template<typename RC>
105  using fn = mp_bool<ComputedField<M, RC>>;
106  };
107 
109  template<typename M>
110  inline constexpr bool allFieldsAreComputed
111  = mp_all_of<LeafRecordCoords<typename M::RecordDim>, MakeIsComputed<M>::template fn>::value;
112 
114  template<typename M>
115  concept FullyComputedMapping = Mapping<M> && allFieldsAreComputed<M>;
116 
118  template<
119  typename M,
120  typename LeafCoords = LeafRecordCoords<typename M::RecordDim>,
121  std::size_t PhysicalCount = mp_count_if<LeafCoords, MakeIsPhysical<M>::template fn>::value,
122  std::size_t ComputedCount = mp_count_if<LeafCoords, MakeIsComputed<M>::template fn>::value>
123  inline constexpr bool allFieldsArePhysicalOrComputed
124  = (PhysicalCount + ComputedCount) >= mp_size<LeafCoords>::value && PhysicalCount > 0
125  && ComputedCount > 0; // == instead of >= would be better, but it's not easy to count correctly,
126  // because we cannot check whether the call to blobNrOrOffset()
127  // or compute() is actually valid
128 
130  template<typename M>
131  concept PartiallyComputedMapping = Mapping<M> && allFieldsArePhysicalOrComputed<M>;
132 
135  template<typename B>
136  concept Blob = requires(B b, std::size_t i) {
137  // according to http://eel.is/c++draft/intro.object#3 only std::byte and unsigned char can
138  // provide storage for
139  // other types
140  requires std::is_lvalue_reference_v<decltype(b[i])>;
141  requires std::same_as<std::remove_cvref_t<decltype(b[i])>, std::byte>
142  || std::same_as<std::remove_cvref_t<decltype(b[i])>, unsigned char>;
143  };
144 
146  template<typename BA>
147  concept BlobAllocator = requires(BA ba, std::size_t size) {
148  {
149  ba(std::integral_constant<std::size_t, 16>{}, size)
150  } -> Blob;
151  };
152 
154  template<typename V>
155  concept AnyView = requires(V v, const V cv) {
156  typename V::Mapping;
157  typename V::BlobType;
158  typename V::ArrayExtents;
159  typename V::ArrayIndex;
160  typename V::RecordDim;
161  typename V::Accessor;
162 
163  typename V::iterator;
164  typename V::const_iterator;
165 
166  {
167  v.mapping()
168  } -> std::same_as<typename V::Mapping&>;
169 
170  {
171  cv.mapping()
172  } -> std::same_as<const typename V::Mapping&>;
173 
174  {
175  v.accessor()
176  } -> std::same_as<typename V::Accessor&>;
177 
178  {
179  cv.accessor()
180  } -> std::same_as<const typename V::Accessor&>;
181 
182  {
183  cv.extents()
184  } -> std::same_as<typename V::ArrayExtents>;
185 
186  {
187  v.begin()
188  } -> std::same_as<typename V::iterator>;
189 
190  {
191  cv.begin()
192  } -> std::same_as<typename V::const_iterator>;
193 
194  {
195  v.end()
196  } -> std::same_as<typename V::iterator>;
197 
198  {
199  cv.end()
200  } -> std::same_as<typename V::const_iterator>;
201 
202  {
203  v.blobs()
204  } -> std::same_as<Array<typename V::BlobType, V::Mapping::blobCount>&>;
205  {
206  cv.blobs()
207  } -> std::same_as<const Array<typename V::BlobType, V::Mapping::blobCount>&>;
208 
209  v(std::declval<typename V::ArrayIndex>());
210  cv(std::declval<typename V::ArrayIndex>());
211  v[std::declval<typename V::ArrayIndex>()];
212  cv[std::declval<typename V::ArrayIndex>()];
213  // operator(i0, i1, ...)
214  // operator[i0, i1, ...]
215  };
216 #endif
217 
218  namespace internal
219  {
220  template<typename R, typename = void>
221  struct IsProxyReferenceImpl : std::false_type
222  {
223  };
224 
225  template<typename R>
227  R,
228  std::void_t<
229  typename R::value_type,
230  decltype(static_cast<typename R::value_type>(std::declval<R&>())),
231  decltype(std::declval<R&>() = std::declval<typename R::value_type>())>>
232  : std::bool_constant<std::is_copy_constructible_v<R> && std::is_copy_assignable_v<R>>
233  {
234  };
235  } // namespace internal
236 
238  template<typename R>
239 #ifdef __cpp_lib_concepts
240  inline constexpr bool isProxyReference = ProxyReference<R>;
241 #else
243 #endif
244 } // namespace llama
#define LLAMA_EXPORT
Definition: macros.hpp:192
constexpr bool isProxyReference
Definition: Concepts.hpp:242
ArrayIndex(Args...) -> ArrayIndex< typename internal::IndexTypeFromArgs< std::size_t, Args... >::type, sizeof...(Args)>
ArrayExtents(Args...) -> ArrayExtents< typename internal::IndexTypeFromArgs< std::size_t, Args... >::type,(Args{}, dyn)... >
constexpr bool isComputed
Returns true if the field accessed via the given mapping and record coordinate is a computed value.
Definition: View.hpp:84
auto swap(RecordRef< ViewA, BoundRecordDimA, OwnViewA > &a, RecordRef< ViewB, BoundRecordDimB, OwnViewB > &b) noexcept -> std::enable_if_t< std::is_same_v< typename RecordRef< ViewA, BoundRecordDimA, OwnViewA >::AccessibleRecordDim, typename RecordRef< ViewB, BoundRecordDimB, OwnViewB >::AccessibleRecordDim >>
Definition: RecordRef.hpp:782