Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Foundation/Memory/Common.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Memory_Common_h_
5#define _Stroika_Foundation_Memory_Common_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <bit>
10#include <compare>
11#include <memory>
12#include <span>
13#include <type_traits>
14
15#include "Stroika/Foundation/Common/Common.h"
16#include "Stroika/Foundation/Common/Concepts.h"
17
18/**
19 */
20
21namespace Stroika::Foundation::Memory {
22
23 namespace Private_ {
24 template <class>
25 inline constexpr bool _Is_span_v = false;
26 template <class _Ty, size_t _Extent>
27 inline constexpr bool _Is_span_v<span<_Ty, _Extent>> = true;
28 }
29 /**
30 * For when you want to assert an argument is a SPAN, but you haven't yet deduced the type its a span of yet.
31 *
32 * \note matches span<T>, span<T,EXTENT>, span<const T>, span<const T,EXTENT>, but not things that
33 * are CONVERTIBLE to span<T>
34 */
35 template <typename SPAN_T>
36 concept ISpan = Private_::_Is_span_v<SPAN_T>;
37 static_assert (ISpan<span<int>> and ISpan<span<int, 3>>);
38 static_assert (not ISpan<std::string> and not ISpan<int>); // we don't include <string> in this module, but sometimes helpful to test/debug/document
39
40 /**
41 * \brief use ISpanOfT<T> as a concept declaration for parameters where you want a span, but accept either T or const T
42 *
43 * Sadly, if I declare a function
44 * f (span<int>) {}
45 * and try to call it with:
46 * f (span<const int>{}) - that fails, whereas I think, considering the logic/intent, it probably should work.
47 *
48 * Anyhow, replacing the f declaration with the (almost as clear);
49 * template <ISpanOfT<int> SPAN_OF_T>
50 * f (SPAN_OF_T) {}
51 *
52 * fixes the problem.
53 *
54 * @aliases
55 * SpanOfPossiblyConstT - but name just too long (though might be clearer).
56 *
57 * note - matches span<T>, span<T,EXTENT>, span<const T>, span<const T,EXTENT>, but not things that
58 *
59 * are CONVERTIBLE to span<T>
60 * \see https://stackoverflow.com/questions/62688814/stdspanconst-t-as-parameter-in-function-template
61 */
62 template <typename SPAN_T, typename T>
63 concept ISpanOfT = Common::IAnyOf<remove_cvref_t<SPAN_T>, span<T>, span<const T>, span<T, SPAN_T::extent>, span<const T, SPAN_T::extent>>;
64 static_assert (ISpanOfT<span<int>, int> and ISpanOfT<span<const int>, int> and ISpanOfT<span<const int, 3>, int> and not ISpanOfT<span<int>, char>);
65
66 /**
67 * \brief convert a (possibly already const) span to the same span, but with const on the 'T' argument
68 *
69 * I hope this function isn't needed often. And wherever it is needed, I think is a std::span<> design
70 * defect (or something I don't yet understand about how to use span better).
71 *
72 * But it appears if you declare function argument span<const T,EXT> and pass in a non-const T span, you get
73 * error messages about failure to deduce (at least on visual studio).
74 *
75 * See https://stackoverflow.com/questions/62688814/stdspanconst-t-as-parameter-in-function-template
76 *
77 * \note this is sometimes useful to reduce deduction ambiguities, and cases where templates convert between
78 * multiple levels of deduction/inference
79 */
80 template <class T, size_t EXTENT>
81 constexpr span<const T, EXTENT> ConstSpan (span<T, EXTENT> s);
82
83 /**
84 * \brief NEltsOf(X) returns the number of elements in array argument (ie return sizeof (arg)/sizeof(arg[0]))
85 *
86 * @todo Found std::begin() could be used to replace old StartOfArray() macro -
87 * see if this too can be replaced with something in C++11?
88 */
89 template <typename ARRAY_TYPE, size_t SIZE_OF_ARRAY>
90 constexpr size_t NEltsOf ([[maybe_unused]] const ARRAY_TYPE (&arr)[SIZE_OF_ARRAY]);
91
92 /**
93 * \brief 'cast' the given POD data type argument to a span<const byte> - a bit like std::as_bytes, but taking different arguments
94 *
95 * \note only works on POD (trivially_v<T>) data
96 * \note returns address of argument, so use results before argument goes out of scope
97 * \note Similar to std::bit_cast<>, but always converts to span of bytes, and doesn't COPY anything - just 'casts' the elt to a span of bytes
98 */
99 template <typename T>
100 span<const byte> AsBytes (const T& elt)
101 requires (is_trivial_v<T>);
102
103 /**
104 * \brief - like std::memcmp() - except count is in units of T (not bytes) and this function is
105 * constexpr, and this function allows nullptr arguments (if count == 0).
106 *
107 * @aliases memcmp, MemCmp
108 *
109 * Pointer Overload:
110 * \pre (count == 0 or lhs != nullptr);
111 * \pre (count == 0 or rhs != nullptr);
112 *
113 * Span Overload:
114 * \pre lhs.size () == rhs.size ()
115 *
116 * \note - like std::memcmp() it returns an int < 0 for less, == 0 for equal, and > 0 for greater, but that corresponds
117 * backward compatibly to the strong_ordering C++20 type, so we use that for clarity going forward.
118 */
119 template <typename T>
120 constexpr strong_ordering CompareBytes (const T* lhs, const T* rhs, size_t count);
121 template <typename T>
122 constexpr strong_ordering CompareBytes (span<const T> lhs, span<const T> rhs);
123 template <typename T>
124 constexpr strong_ordering CompareBytes (span<T> lhs, span<T> rhs);
125
126 /**
127 * \brief return true iff intersection of the two spans is non-empty (contains any bytes)
128 *
129 * Note this is similar to Range::Intersects, except for the business about openness/closedness and details at the edge conditions
130 *
131 * The only known use for this is assertions in CopySpanData that the spans don't overlap (memcpy vs memmove)
132 */
133 template <typename T1, typename T2, size_t E1, size_t E2>
134 constexpr bool Intersects (span<T1, E1> lhs, span<T2, E2> rhs);
135
136 /**
137 * \brief Can safely cast span<FROM_T,FROM_EXTENT> to a TO_SPAN (where the underlying types are POD - plain old data - types - roughly)
138 *
139 * \note - this requires the two spans to have the same number of bytes (cannot always be fully determined at compile time).
140 * But this returns true if its possible.
141 *
142 * \note its perfectly reasonable to span cast from span<uint32_t> to span<byte> - so long as the two spans have the same size_bytes()
143 *
144 * \note this also requires trivially_copyable on the types. Nothing REALLY requires that. But its more likely a bug than
145 * a feature if you are using types for which that is not true, so fail here. And force a more careful exam with explicit
146 * reinterpret_casts...
147 *
148 * \note Since Stroika v3.0d15 - this doesn't allow casting away constness of the underlying value_type (though it ignores the
149 * constness of the span itself).
150 */
151 template <typename TO_SPAN, typename FROM_SPAN>
154 (is_const_v<typename TO_SPAN::value_type> or not is_const_v<typename FROM_SPAN::value_type>) and
155 (sizeof (typename FROM_SPAN::value_type) % sizeof (typename TO_SPAN::value_type) == 0 or
156 sizeof (typename TO_SPAN::value_type) % sizeof (typename FROM_SPAN::value_type) == 0);
157
158 /**
159 * \brief 'cast' a span of one thing to another, as if as_bytes, from_bytes; require span<T1...> and span<T2...> such that one T size is a multiple of the other
160 *
161 * \pre ((src.size_bytes () / sizeof (TO_T)) * sizeof (TO_T) == src.size_bytes ()); - so this doesn't change size in bytes of span
162 *
163 * This requirement on the same size in bytes of elements sizeof FROM_T must evenly divide sizeof TO_T (or the reverse).
164 * This is to allow the returned span{} to cover the same number of bytes.
165 *
166 * \note - TO_T == std::byte, this is the same as std::as_bytes or std::as_writable_bytes
167 *
168 * \post resulting span same size_bytes () as src.size_bytes().
169 *
170 * \note Though this CAN be used with fixed-extent spans, the caller must then specify the fixed extent,
171 * which must be correct. Probably works most simply if the EXTENT in the TO_SPAN is dynamic_extent (or omitted).
172 *
173 * \note until Stroika v3.0d12 this was called SpanReInterpretCast
174 *
175 * \note this allows compiling SpanBytesCast<span<uint32_t>> (span<byte>{}) - which may work fine, or may assert out if size of argument
176 * not divisible by 4 (EXAMPLE).
177 */
178 template <ISpan TO_SPAN, ISpanBytesCastable<TO_SPAN> FROM_SPAN>
179 constexpr TO_SPAN SpanBytesCast (FROM_SPAN src);
180
181 /**
182 * \brief Span-flavored memcpy/std::copy (copies from, to) - requires argument spans not overlap, requires src.size <= to.size()
183 *
184 * like std::copy, except copies the data the spans point to/reference. Target span maybe larger than src,
185 * but must (require) be no smaller than src span;
186 *
187 * \pre src.size () <= target.size () -- so that all of source can always be copied (else would need api/indicator of how much copied)
188 * \pre not Intersects (src, target) - so non-overlapping
189 *
190 * \note somewhat unlike memcpy, its fine if the spans{} are empty ()
191 *
192 * Returns the subset of the target span filled (so a subspan of target).
193 *
194 * @see also CopyOverlappingBytes for 'memmove' - same API but where the data can overlap
195 */
196 template <Common::trivially_copyable FROM_T, size_t FROM_E, Common::trivially_copyable TO_T, size_t TO_E>
197 constexpr span<TO_T, TO_E> CopyBytes (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target) noexcept
198 requires (same_as<remove_cvref_t<FROM_T>, remove_cvref_t<TO_T>>);
199
200 /*
201 * \brief Span-flavored std::copy (copies from, to), works with spans, not iterators, works with different sized from/to types
202 *
203 * Similar to CopyBytes, but works with non-trivially copyable data, as well as with differently sized
204 * data (strides), where the individuals are preserved (fully type-safe); No requirement in size_bytes on the two
205 * spans, just on the SIZE of the two spans.
206 *
207 * \pre from.size() <= to.size()
208 * \pre not Intersects (src, target) - so non-overlapping
209 *
210 * \returns the subspan of the target which was just filled in.
211 *
212 * \note @todo CONSIDER how similar/different this is from ranges::copy, or ranges_uninitialized_copy
213 *
214 * \note sizeof (FROM_T) may differ from sizeof (TO_T). So this can be used to downshift char16_t data
215 * to plain ASCII so long as the caller assures the source data is truly ascii first.
216 *
217 * \note WAS CALLED CopySpanData_StaticCast - briefly - until 3.0d12
218 */
219 template <typename FROM_T, size_t FROM_E, typename TO_T, size_t TO_E>
220 constexpr span<TO_T, TO_E> CopySpanData (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target)
221 requires (not is_const_v<TO_T>);
222
223 /**
224 * \brief Span-flavored memmove/std::copy_backwards (copies from, to) - ALLOWING argument spans to overlap
225 *
226 * like std::copy_backward, except copies the data the spans point to/reference. Target span maybe larger than src,
227 * but must (require) be no smaller than src span;
228 *
229 * \pre src.size () <= target.size () -- so that all of source can always be copied (else would need api/indicator of how much copied)
230 *
231 * Returns the subset of the target span filled (so a subspan of target).
232 *
233 * @see also CopyBytes
234 */
235 template <Common::trivially_copyable FROM_T, size_t FROM_E, Common::trivially_copyable TO_T, size_t TO_E>
236 constexpr span<TO_T, TO_E> CopyOverlappingBytes (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target) noexcept
237 requires (same_as<remove_cvref_t<FROM_T>, remove_cvref_t<TO_T>>);
238
239 /**
240 * \brief use Memory::OffsetOf(&CLASS::MEMBER) in place of offsetof(CLASS,MEMBER) to avoid compiler warnings, and cuz easier to
241 * map from other constructors (e.g. StructFieldMetaInfo) cuz ptr to member legit C++ object, whereas CLASS and MEMBER are not.
242 *
243 * REPLACE calls to:
244 * offsetof (CLASS, MEMBER)
245 * with:
246 * OffsetOf (&CLASS::MEMBER)
247 *
248 * \note The current implementation exploits UNDEFINED BEHAVIOR.
249 *
250 * expr.add-5.sentence-2
251 * "If the expressions P and Q point to, respectively, elements x[i] and x[j] of
252 * the same array object x, the expression P - Q has the value i - j; otherwise, the behavior is undefined.
253 *
254 * \par Example Usage
255 * \code
256 * struct Person {
257 * String firstName;
258 * String lastName;
259 * };
260 * constexpr size_t kOffset_ = OffsetOf(&Person::lastName);
261 * static_assert (OffsetOf (&Person::firstName) == 0); // NOTE - we WANT this to work, but for now cannot get constexpr stuff working
262 * \endcode
263 *
264 * \par Example Usage
265 * \code
266 * struct X1 {
267 * int a;
268 * int b;
269 * };
270 * void DoTest ()
271 * {
272 * assert (OffsetOf (&X1::a) == 0);
273 * assert (OffsetOf (&X1::b) >= sizeof (int));
274 * }
275 * \endcode
276 *
277 * @see https://gist.github.com/graphitemaster/494f21190bb2c63c5516
278 * @see https://en.cppreference.com/w/cpp/types/offsetof
279 * @see https://stackoverflow.com/questions/65940393/c-why-the-restriction-on-offsetof-for-non-standard-layout-objects-or-how-t
280 *
281 * \note Tricky to get this to work with constexpr. See implementation for details.
282 */
283 template <typename OUTER_OBJECT, typename DATA_MEMBER_TYPE>
284 constexpr size_t OffsetOf (DATA_MEMBER_TYPE (OUTER_OBJECT::* dataMember));
285
286 /**
287 * \brief UninitializedConstructorFlag::eUninitialized is a flag to some memory routines to allocate without initializing
288 *
289 * This is mainly used as a performance tweak, for objects that don't need to be initialized, and can just be copied into.
290 *
291 * \note the APIS that use this typically require static_assert (is_trivially_copyable_v<T>);
292 */
293 enum class UninitializedConstructorFlag {
294 eUninitialized,
295 eUninitializedIfTrivial
296 };
297
298 /**
299 * \brief eUninitialized is a flag to some memory routines to allocate without initializing (static_assert T is trivial)
300 *
301 * \see UninitializedConstructorFlag
302 */
303 using UninitializedConstructorFlag::eUninitialized;
304
305 /**
306 * \brief eUninitialized is a flag to some memory routines to allocate without initializing (if T is not trivial)
307 *
308 * \see UninitializedConstructorFlag
309 */
310 using UninitializedConstructorFlag::eUninitializedIfTrivial;
311
312 inline namespace Literals {
313 /**
314 * \brief A utility for declaring constant bytes (byte literals).
315 *
316 * \pre b <= 0xff
317 *
318 * \see https://stackoverflow.com/questions/75411756/how-do-i-declare-and-initialize-an-array-of-bytes-in-c
319 */
320 constexpr byte operator"" _b (unsigned long long b);
321 }
322
323 /**
324 * API to return memory allocation statistics. Generally - these will be inaccurate,
325 * unless certain defines are set in Memory.cpp - but at least some stats can be
326 * returned in either case.
327 *
328 * Note - this currently only produces useful answers on windows, but can easily pull
329 * similar values out of /proc fs stuff with linux (nyi).
330 *
331 * @todo Does this belong in "Execution" module"
332 */
334 constexpr GlobalAllocationStatistics () = default;
335
336 size_t fTotalOutstandingAllocations{0};
337 size_t fTotalOutstandingBytesAllocated{0};
338 size_t fPageFaultCount{0};
339 size_t fWorkingSetSize{0};
340 size_t fPagefileUsage{0};
341 };
342 GlobalAllocationStatistics GetGlobalAllocationStatistics ();
343
344}
345
346/*
347 ********************************************************************************
348 ***************************** Implementation Details ***************************
349 ********************************************************************************
350 */
351#include "Common.inl"
352
353#endif /*_Stroika_Foundation_Memory_Common_h_*/
concept - trivial shorthand for variadic same_as A or same_as B, or ...
Definition Concepts.h:175
concept version of std::is_trivially_copyable_v
Definition Concepts.h:181
Can safely cast span<FROM_T,FROM_EXTENT> to a TO_SPAN (where the underlying types are POD - plain old...
use ISpanOfT<T> as a concept declaration for parameters where you want a span, but accept either T or...