Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Foundation/Memory/Common.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include <algorithm>
5#include <cstring>
6
10
11namespace Stroika::Foundation::Memory {
12
13 /*
14 ********************************************************************************
15 ********************************* Memory::NEltsOf ******************************
16 ********************************************************************************
17 */
18 template <typename ARRAY_TYPE, size_t SIZE_OF_ARRAY>
19 inline constexpr size_t NEltsOf ([[maybe_unused]] const ARRAY_TYPE (&arr)[SIZE_OF_ARRAY])
20 {
21 return SIZE_OF_ARRAY;
22 }
23
24 /*
25 ********************************************************************************
26 *************************** Memory::CompareBytes *******************************
27 ********************************************************************************
28 */
29 template <>
30 constexpr strong_ordering CompareBytes (const uint8_t* lhs, const uint8_t* rhs, size_t count)
31 {
32 if (is_constant_evaluated ()) {
33 //Require (count == 0 or lhs != nullptr);
34 //Require (count == 0 or rhs != nullptr);
35 const uint8_t* li = lhs;
36 const uint8_t* ri = rhs;
37 for (; count--; ++li, ++ri) {
38 if (int cmp = static_cast<int> (*li) - static_cast<int> (*ri)) {
40 }
41 }
42 return strong_ordering::equal;
43 }
44 else {
45 if (count == 0) [[unlikely]] {
46 return strong_ordering::equal;
47 }
48 return Common::CompareResultNormalizer (::memcmp (lhs, rhs, count));
49 }
50 }
51 template <typename T>
52 constexpr strong_ordering CompareBytes (const T* lhs, const T* rhs, size_t count)
53 {
54 return CompareBytes (reinterpret_cast<const uint8_t*> (lhs), reinterpret_cast<const uint8_t*> (rhs), count * sizeof (T));
55 }
56 template <typename TL, size_t EL, typename TR, size_t ER>
57 constexpr strong_ordering CompareBytes (span<TL, EL> lhs, span<TR, ER> rhs)
58 requires (same_as<remove_cvref_t<TL>, remove_cvref_t<TR>> and is_trivial_v<TL>)
59 {
60 Require (lhs.size () == rhs.size ());
61 return CompareBytes (lhs.data (), rhs.data (), lhs.size ());
62 }
63
64 /*
65 ********************************************************************************
66 ******************************** Memory::AsBytes *******************************
67 ********************************************************************************
68 */
69 template <typename T>
70 inline span<const byte> AsBytes (const T& elt)
71 requires (is_trivial_v<T>)
72 {
73 return as_bytes (span{&elt, 1}); //return span<const byte>{reinterpret_cast<const byte*> (&elt), sizeof (elt)};
74 }
75
76 /*
77 ********************************************************************************
78 ***************************** Memory::ConstSpan ********************************
79 ********************************************************************************
80 */
81 template <class T, size_t EXTENT>
82 constexpr span<const T, EXTENT> ConstSpan (span<T, EXTENT> s)
83 {
84 return s;
85 }
86
87 /*
88 ********************************************************************************
89 ***************************** Memory::Intersects *******************************
90 ********************************************************************************
91 */
92 template <typename T1, typename T2, size_t E1, size_t E2>
93 constexpr bool Intersects (span<T1, E1> lhs, span<T2, E2> rhs)
94 {
95 // See Range<T, TRAITS>::Intersects for explanation - avoid direct call here to avoid include file reference
96 /*
97 * Assume LHS is at given position. RHS could be A, B, C, D, E, F, G, or H
98 * | LHS |
99 * | A | | B | | C | | D | | E |
100 * | G | | H |
101 * | F |
102 */
103 if (lhs.empty () or rhs.empty ()) {
104 return false;
105 }
106 const byte* lhsStart = addressof (*as_bytes (lhs).data ());
107 const byte* rhsStart = addressof (*as_bytes (rhs).data ());
108 const byte* lhsEnd = lhsStart + lhs.size_bytes ();
109 const byte* rhsEnd = rhsStart + rhs.size_bytes ();
110 // NOTE - not sure how to test this safely in general. On segmented architectures, this test isn't legal, if they are not
111 // from the same segment, for example. C++ only guarantees test safe IF they overlap, in essence --LGP 2025-04-29
112 if (rhsEnd <= lhsStart) {
113 // cases A, G
114 return false; // combine two cases from Range<T, TRAITS>::Intersects cuz always closed
115 }
116 if (rhsStart >= lhsEnd) {
117 // cases E, H
118 return false; // ""
119 }
120 // cases B, C, D, F
121 return true;
122 }
123
124 /*
125 ********************************************************************************
126 ***************************** Memory::SpanBytesCast ****************************
127 ********************************************************************************
128 */
129 template <ISpan TO_SPAN, ISpanBytesCastable<TO_SPAN> FROM_SPAN>
130 constexpr TO_SPAN SpanBytesCast (FROM_SPAN src)
131 {
132 using TO_T = typename TO_SPAN::value_type;
133 Require ((src.size_bytes () / sizeof (TO_T)) * sizeof (TO_T) == src.size_bytes ()); // else cannot map evenly
134 // allow EITHER size or constness conversions - so NOT re-interpret cast
135 TO_SPAN result{(TO_T*)(src.data ()), src.size_bytes () / sizeof (TO_T)}; // no need to worry about overflow cuz then addressses would overflow
136 Ensure (src.size_bytes () == result.size_bytes ()); // size in elements can be <, ==, or >
137 return result;
138 }
139
140 /*
141 ********************************************************************************
142 ***************************** Memory::CopyBytes ********************************
143 ********************************************************************************
144 */
145 template <Common::trivially_copyable FROM_T, size_t FROM_E, Common::trivially_copyable TO_T, size_t TO_E>
146 constexpr span<TO_T, TO_E> CopyBytes (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target) noexcept
147 requires (same_as<remove_cv_t<FROM_T>, TO_T>)
148 {
149 Require (src.size () <= target.size ());
150 Require (not Intersects (src, target));
151 copy (src.begin (), src.end (), target.data ());
152 return target.subspan (0, src.size ());
153 }
154
155 /*
156 ********************************************************************************
157 ************************ Memory::CopyOverlappingBytes **************************
158 ********************************************************************************
159 */
160 template <Common::trivially_copyable FROM_T, size_t FROM_E, Common::trivially_copyable TO_T, size_t TO_E>
161 constexpr span<TO_T, TO_E> CopyOverlappingBytes (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target) noexcept
162 requires (same_as<remove_cv_t<FROM_T>, TO_T>)
163 {
164 Require (src.size () <= target.size ());
165 if (src.size () == 0) {
166 Assert (target.size () == 0);
167 return target.subspan (0, 0);
168 }
169 // When copying overlapping ranges, std::copy is appropriate when copying to the left (beginning of the
170 // destination range is outside the source range) while std::copy_backward is appropriate when copying
171 // to the right (end of the destination range is outside the source range).
172 if (Intersects (src, target)) {
173 span<byte> targetBytes = as_writable_bytes (target);
174 span<const byte> srcBytes = as_bytes (src);
175 // we know they overlap, so just checking which is to the left and which to the right
176 if (addressof (*targetBytes.data ()) >= addressof (*srcBytes.data ())) {
177 // target inside src-range, so copy_backward
178 copy_backward (srcBytes.data (), srcBytes.data () + srcBytes.size (), targetBytes.data () + srcBytes.size ());
179 }
180 else {
181 copy (src.begin (), src.end (), target.data ());
182 }
183 return target.subspan (0, src.size ());
184 }
185 else {
186 return CopyBytes (src, target);
187 }
188 }
189
190 /*
191 ********************************************************************************
192 ****************************** Memory::CopySpanData ****************************
193 ********************************************************************************
194 */
195 template <typename FROM_T, size_t FROM_E, typename TO_T, size_t TO_E>
196 constexpr span<TO_T, TO_E> CopySpanData (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target)
197 requires (not is_const_v<TO_T>)
198 {
199 Require (not Intersects (src, target));
200 Require (src.size () <= target.size ()); // BUT size in BYTES need not match
201 if constexpr (Common::trivially_copyable<TO_T> and Common::trivially_copyable<FROM_T> and sizeof (FROM_T) == sizeof (TO_T)) {
202 // if elements trivially copyable, and STRIDE of elements same on both sides, can copy as bytes
203 return CopyBytes (SpanBytesCast<span<const TO_T, TO_E>> (src), target);
204 }
205 else {
206 // Do a for loop copying each element; this can be used to copy any data,
207 // like std::copy, except using spans not iterators, and no warning/error about target assignment (static_cast)
208 TO_T* tb = target.data ();
209 for (const FROM_T& i : src) {
210 *tb++ = static_cast<TO_T> (i);
211 }
212 return target.subspan (0, src.size ());
213 }
214 }
215
216 /*
217 ********************************************************************************
218 ********************* Memory::CopyOverlappingSpanData **************************
219 ********************************************************************************
220 */
221 template <typename T, size_t FROM_E, size_t TO_E>
222 constexpr span<T, TO_E> CopyOverlappingSpanData (span<T, FROM_E> src, span<T, TO_E> target)
223 requires (not is_const_v<T>)
224 {
225 Require (src.size () == target.size ());
226 if (target.size () != 0) {
227 if (Intersects (src, target)) {
228 // When copying overlapping ranges, std::copy is appropriate when copying to the left (beginning of the
229 // destination range is outside the source range) while std::copy_backward is appropriate when copying
230 // to the right (end of the destination range is outside the source range).
231 if (addressof (*target.data ()) >= addressof (*src.data ())) {
232 // target inside src-range, so copy_backward
233 size_t nItems2Copy = src.size ();
234 copy_backward (src.data (), src.data () + nItems2Copy, target.data () + nItems2Copy);
235 }
236 else {
237 copy (src.begin (), src.end (), target.data ());
238 }
239 }
240 else {
241 return CopySpanData (src, target);
242 }
243 }
244 return target;
245 }
246
247 /*
248 ********************************************************************************
249 ********************************* Memory::Insert *******************************
250 ********************************************************************************
251 */
252 template <ISpan INTO_SPAN, ISpan FROM_SPAN>
253 requires (same_as<remove_const_t<typename INTO_SPAN::value_type>, remove_const_t<typename FROM_SPAN::value_type>>)
254 remove_cvref_t<INTO_SPAN> Insert (const INTO_SPAN& intoLiveSpan, const INTO_SPAN& intoReservedSpan, size_t at, const FROM_SPAN& copyFrom) noexcept
255 {
256 using T = remove_cvref_t<typename INTO_SPAN::value_type>;
257 Require (intoLiveSpan.data () == intoReservedSpan.data () or (intoLiveSpan.size () == 0));
258 size_t n2Add = copyFrom.size ();
259 Require (intoLiveSpan.size () + copyFrom.size () <= intoReservedSpan.size ());
260 Require (at + copyFrom.size () <= intoReservedSpan.size ());
261 T* b = intoReservedSpan.data ();
262 //
263 // at origSize
264 // v v
265 // [.....orig data... ...more data...]
266 //
267 // becomes (; slide by newS; but last newS elts uninitialized_copy copy, and regular copy rest)
268 //
269 // at at+n2Add
270 // v v
271 // [.....orig data... {copyFrom} ...more data...]
272 //
273 //
274 size_t origSize = intoLiveSpan.size ();
275 size_t newSize = origSize + n2Add;
276 Assert (newSize > 0);
277
278 if constexpr (is_trivially_copyable_v<T>) {
279 // we don't need to pay attention to what is initialized and what is not so quicker and easier.
280 // So slosh bytes after at down, and copy in the new ones
281 CopyOverlappingBytes (span{b + at, origSize - at}, span{b + at + n2Add, origSize - at});
282#if qCompilerAndStdLib_stdlib_ranges_pretty_broken_Buggy
283 uninitialized_copy (copyFrom.begin (), copyFrom.end (), b + at);
284#else
285 ranges::uninitialized_copy (copyFrom, span{b + at, n2Add});
286#endif
287 }
288 else {
289 // Simple but not super algorithm, append the data (using uninitialized_copy)
290 // and then std::rotate () - idea lifted from MSVC std::vector::insert()
291 // reason for trixyness, is cuz we need uninitialized_copy for new stuff (into uninitialized memory)
292 // and copy with destruction of old stuff for rest (handled by rotate)
293#if qCompilerAndStdLib_stdlib_ranges_pretty_broken_Buggy
294 uninitialized_copy (copyFrom.begin (), copyFrom.end (), b + origSize);
295#else
296 ranges::uninitialized_copy (copyFrom, span{b + origSize, n2Add});
297#endif
298 rotate (b + at, b + origSize, b + newSize);
299 }
300 return intoReservedSpan.subspan (0, newSize);
301 }
302
303 /*
304 ********************************************************************************
305 ********************************* Memory::Remove *******************************
306 ********************************************************************************
307 */
308 template <ISpan FROM_SPAN>
309 requires (not is_const_v<typename FROM_SPAN::value_type>)
310 remove_cvref_t<FROM_SPAN> Remove (FROM_SPAN&& spanToEdit, FROM_SPAN&& reservedSpan, size_t from, size_t to) noexcept
311 {
312 // @todo FIX - this COPIES elements during destroy, but should MOVE THEM - slight performance optimization sometimes
313 using T = remove_cvref_t<typename FROM_SPAN::value_type>;
314 Require (spanToEdit.data () == reservedSpan.data () or (spanToEdit.data () == nullptr and spanToEdit.size () == 0));
315 Require (from <= to);
316 Require (to <= spanToEdit.size ());
317 T* b = reservedSpan.data ();
318 size_t amountRemoved = to - from;
319
320 // if removing from anything BUT the end of the (live) buffer, we must slide items down
321 if (to < spanToEdit.size ()) {
322 /*
323 * Slide items down. Note - this operates ENTIRELY on LIVE (constructed) objects.
324 * b b+from b+to
325 * v v v
326 * | .... RANGE2REMOVE|STUFF-AFTER|END-OF-BUFFER
327 * produces
328 * | .....STUFF-AFTER|END-OF-BUFFER
329 * /\
330 * b+spanToEdit.size()-amountRemoved
331 */
332 auto copySrcSpan = span{b + to, b + spanToEdit.size ()}; // STUFF-AFTER in first line
333 auto copyToSpan = span{b + from, copySrcSpan.size ()}; // RANGE2REMOVE in first line
334 Memory::CopyOverlappingSpanData (copySrcSpan, copyToSpan);
335 }
336 // but either way, we must destroy a few elements at the end, and return the updated span
337 auto destroySpan = span{b + spanToEdit.size () - amountRemoved, amountRemoved};
338#if qCompilerAndStdLib_stdlib_ranges_pretty_broken_Buggy
339 destroy (destroySpan.begin (), destroySpan.end ());
340#else
341 ranges::destroy (destroySpan);
342#endif
343 return reservedSpan.subspan (0, spanToEdit.size () - amountRemoved);
344 }
345
346 /*
347 ********************************************************************************
348 ******************************** Memory::OffsetOf ******************************
349 ********************************************************************************
350 */
351 namespace Private_ {
352 namespace OffsetOfImpl_ {
353 namespace UsingRecursiveSideStruct_ {
354 // OffsetOf_ BASED ON CODE FROM - https://gist.github.com/graphitemaster/494f21190bb2c63c5516
355#pragma pack(push, 1)
356 template <typename MEMBER, size_t N_PAD_BYTES>
357 struct Pad {
358 char pad[N_PAD_BYTES];
359 MEMBER m;
360 };
361#pragma pack(pop)
362 template <typename MEMBER>
363 struct Pad<MEMBER, 0> {
364 MEMBER m;
365 };
366
367 DISABLE_COMPILER_MSC_WARNING_START (4324);
368 template <typename BASE, typename MEMBER, size_t O>
369 struct MakeUnion {
370 union U {
371 char c;
372 BASE base;
373 Pad<MEMBER, O> pad;
374 constexpr U () noexcept
375 : c{} {};
376 ~U () = delete;
377 };
378 //constexpr static U* u{}; // old code did this, but doesn't work if MEMBER field has type which is not allowed to be constexpr --LGP 2023-08-20
379 constexpr static U* u{nullptr}; // don't actually allocate an object (maybe use declval instead?) - cuz else U not literal type sometimes if MEMBER obj type not literal
380 };
381 DISABLE_COMPILER_MSC_WARNING_END (4324);
382
383 template <typename MEMBER, typename BASE_CLASS, typename ORIG_CLASS>
384 struct offset_of_impl {
385 template <size_t off, auto union_part = MakeUnion<BASE_CLASS, MEMBER, off>::u>
386 // This gets called with nullptr as 'object' for computing diff below to avoid ever building the object (cuz just computing offsets)
387 Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_UNDEFINED static constexpr ptrdiff_t offset2 (MEMBER ORIG_CLASS::* member)
388 {
389 if constexpr (off > sizeof (BASE_CLASS)) {
390 throw 1;
391 }
392 else {
393 const auto diff1 = &((static_cast<const ORIG_CLASS*> (&union_part->base))->*member);
394 const auto diff2 = &union_part->pad.m;
395 if (diff1 > diff2) {
396 constexpr auto MIN = (sizeof (MEMBER) < alignof (ORIG_CLASS)) ? sizeof (MEMBER) : alignof (ORIG_CLASS);
397 return offset2<off + MIN> (member);
398 }
399 else {
400 return off;
401 }
402 }
403 }
404 };
405
406 template <class MEMBER, class BASE_CLASS>
407 tuple<MEMBER, BASE_CLASS> get_types (MEMBER BASE_CLASS::*); // never defined, never really called, just used to extract types with decltype()
408
409 template <class TheBase = void, class TT>
410 inline constexpr size_t offset_of (TT member)
411 {
412 using T = decltype (get_types (declval<TT> ()));
413 using Member = tuple_element_t<0, T>;
414 using Orig = tuple_element_t<1, T>;
415 using Base = conditional_t<is_void_v<TheBase>, Orig, TheBase>;
416 return static_cast<size_t> (offset_of_impl<Member, Base, Orig>::template offset2<0> (member));
417 }
418 }
419
420 namespace RequiringDefaultConstructibleObjectType_ {
421 // @see https://gist.github.com/graphitemaster/494f21190bb2c63c5516 for more info on maybe how to
422 template <typename T1, typename T2>
423 struct offset_of {
424 static constexpr size_t offset (T1 T2::* member)
425 {
426 union X {
427 array<char, sizeof (T2)> bytes;
428 T2 obj;
429 X () {};
430 ~X () {};
431 } objAsUnion;
432 /*
433 * &&& maybe not undefined anymore
434 * UNDEFINED BEHAVIOR: it is undefined, but for the following reason: expr.add-5.sentence-2
435 * "If the expressions P and Q point to, respectively, elements x[i] and x[j] of
436 * the same array object x, the expression P - Q has the value i - j; otherwise, the behavior is undefined."]
437 */
438 return size_t (&(objAsUnion.obj.*member)) - size_t (&objAsUnion.obj);
439 }
440 };
441 }
442
443 namespace UsingAlignedByteArrayBuf_ {
444 template <typename FIELD_VALUE_TYPE, typename OWNING_OBJECT>
445 inline size_t offset_of (FIELD_VALUE_TYPE OWNING_OBJECT::* member)
446 {
447 // Still not totally legal for non-std-layout classes, but seems to work, and I haven't found a better way
448 // --LGP 2021-05-27
449 alignas (OWNING_OBJECT) byte buf[sizeof (OWNING_OBJECT)]{};
450 const OWNING_OBJECT& o = *reinterpret_cast<const OWNING_OBJECT*> (&buf);
451 auto result = size_t (reinterpret_cast<const char*> (&(o.*member)) - reinterpret_cast<const char*> (&o));
452 // Avoid #include - Ensure (result <= sizeof (OWNING_OBJECT));
453 return result;
454 }
455 }
456
457 namespace UseExplicitDefaultConstructibleStaticInstance_ {
458 // @see https://gist.github.com/graphitemaster/494f21190bb2c63c5516 for more info on maybe how to
459 // get this working with constexpr and without static object
460 template <typename T1, typename T2>
461 struct offset_of_ {
462 static inline constexpr T2 sObj_{};
463 static constexpr size_t offset (T1 T2::* member)
464 {
465 /*
466 * UNDEFINED BEHAVIOR: it is undefined, but for the following reason: expr.add-5.sentence-2
467 * "If the expressions P and Q point to, respectively, elements x[i] and x[j] of
468 * the same array object x, the expression P - Q has the value i - j; otherwise, the behavior is undefined."]
469 */
470 return size_t (&(offset_of_<T1, T2>::sObj_.*member)) - size_t (&offset_of_<T1, T2>::sObj_);
471 }
472 };
473 }
474
475 namespace UsingSimpleUnionToConstructActualObj_ {
476
477 template <typename OUTER_OBJECT, typename DATA_MEMBER_TYPE>
478 inline constexpr size_t offset_of (DATA_MEMBER_TYPE (OUTER_OBJECT::* dataMember))
479 {
480 // NOT real assert - just tmphack to test
481 // auto a1 = Private_::OffsetOfImpl_::UsingRecursiveSideStruct_::offset_of<OUTER_OBJECT> (dataMember);
482 // auto a2 = PRIVATE_::OffsetOfImpl_::UsingAlignedByteArrayBuf_ (dataMember);
483 // auto a3 = PRIVATE_::OffsetOfImpl_::RequiringDefaultConstructibleObjectType_::offset_of<DATA_MEMBER_TYPE, OUTER_OBJECT>::offset (dataMember);
484 //WeakAssert (a1 == a2);
485
486 union X {
487 array<char, sizeof (OUTER_OBJECT)> bytes;
488 OUTER_OBJECT obj;
489 X () {};
490 ~X () {};
491 } objAsUnion;
492
493 // auto a4 = size_t (&(objAsUnion.obj.*dataMember)) - size_t (&objAsUnion.bytes);
494
495 /*
496 * &&& maybe not undefined anymore
497 * UNDEFINED BEHAVIOR: it is undefined, but for the following reason: expr.add-5.sentence-2
498 * "If the expressions P and Q point to, respectively, elements x[i] and x[j] of
499 * the same array object x, the expression P - Q has the value i - j; otherwise, the behavior is undefined."]
500 */
501 return size_t (&(objAsUnion.obj.*dataMember)) - size_t (&objAsUnion.obj);
502
503 //https://stackoverflow.com/questions/12141446/offset-from-member-pointer-without-temporary-instance
504 // return Private_::OffsetOf2_::offset_of<OUTER_OBJECT> (dataMember);
505 }
506
507 }
508 }
509 }
510
511 template <typename OUTER_OBJECT, typename DATA_MEMBER_TYPE>
512 inline constexpr size_t OffsetOf (DATA_MEMBER_TYPE (OUTER_OBJECT::* dataMember))
513 {
514 //constexpr bool kTestAllWays_ = false;
515 constexpr bool kTestAllWays_ = true;
516 [[maybe_unused]] size_t r1;
517 [[maybe_unused]] size_t r2;
518 [[maybe_unused]] size_t r3;
519 [[maybe_unused]] size_t r4;
520 if constexpr (kTestAllWays_) {
521 /*
522 * Setup to test/try each implementation. One with apparently best shot of working constexpr is UsingRecursiveSideStruct_
523 * HOWEVER, it requires (on unix) setting -ftemplate-depth=5000 flags, and even then, doesn't really work constexpr (complains about dereferencing nullptr).
524 *
525 * Don't give up, but no success so far. MAYBE works OK/portably without the constexpr stuff. Dunno. Probably NOT for the same deref-nullptr reason.
526 */
527#if defined(_MSC_VER)
528 // Don't bother compiling for gcc/clang cuz fails on some compilers without -fdepth= flag, and no point since wont really work right even
529 // if you provide that flag. REVISIT in the future maybe, but for now don't even bother calling
530 r1 = Private_::OffsetOfImpl_::UsingRecursiveSideStruct_::offset_of<OUTER_OBJECT> (dataMember);
531#endif
532 r2 = Private_::OffsetOfImpl_::RequiringDefaultConstructibleObjectType_::offset_of<DATA_MEMBER_TYPE, OUTER_OBJECT>::offset (dataMember);
533 r3 = Private_::OffsetOfImpl_::UsingAlignedByteArrayBuf_::offset_of<DATA_MEMBER_TYPE, OUTER_OBJECT> (dataMember);
534 if constexpr (is_trivially_default_constructible_v<OUTER_OBJECT>) {
535 r4 = Private_::OffsetOfImpl_::UseExplicitDefaultConstructibleStaticInstance_::offset_of_<DATA_MEMBER_TYPE, OUTER_OBJECT>::offset (dataMember);
536 }
537 }
538 size_t r5 = Private_::OffsetOfImpl_::UsingSimpleUnionToConstructActualObj_::offset_of<OUTER_OBJECT, DATA_MEMBER_TYPE> (dataMember);
539 size_t rr = r5;
540 if (not is_constant_evaluated () and kTestAllWays_) {
541#if defined(_MSC_VER)
542 Assert (r1 == rr);
543#endif
544 Assert (r2 == rr);
545 Assert (r3 == rr);
546 if constexpr (is_trivially_default_constructible_v<OUTER_OBJECT>) {
547 Assert (r4 == rr);
548 }
549 }
550 return rr;
551 }
552
553 /*
554 ********************************************************************************
555 ********************* Memory::Literals::operator""_b ***************************
556 ********************************************************************************
557 */
558 inline namespace Literals {
559 constexpr byte operator""_b (unsigned long long b)
560 {
561 Require (b <= 0xff);
562 return static_cast<byte> (b);
563 }
564 }
565
566 ////////////////////// DEPRECATED .//////////////////////////
567 template <typename FIELD_VALUE_TYPE, typename OWNING_OBJECT>
568 [[deprecated ("Since Stroika v3.0d2 - just use OffsetOf")]] inline size_t constexpr OffsetOf_Constexpr (FIELD_VALUE_TYPE OWNING_OBJECT::* member)
569 {
570 return OffsetOf (member);
571 }
572 template <typename T>
573 [[deprecated ("Since Stroika v3.0d12 - use CompareBytes")]] constexpr strong_ordering MemCmp (const T* lhs, const T* rhs, size_t count)
574 {
575 return CompareBytes (lhs, rhs, count);
576 }
577 template <typename T>
578 [[deprecated ("Since Stroika v3.0d12 - use CompareBytes")]] constexpr strong_ordering MemCmp (span<const T> lhs, span<const T> rhs)
579 {
580 return CompareBytes (lhs, rhs);
581 }
582 template <typename T>
583 [[deprecated ("Since Stroika v3.0d12 - use CompareBytes")]] constexpr strong_ordering MemCmp (span<T> lhs, span<T> rhs)
584 {
585 return CompareBytes (lhs, rhs);
586 }
587 template <typename FROM_T, size_t FROM_E, typename TO_T, size_t TO_E>
588 [[deprecated ("Since Stroika v3.0d12 use CopyBytes")]] constexpr span<TO_T, TO_E> CopySpanData_StaticCast (span<const FROM_T, FROM_E> src,
589 span<TO_T, TO_E> target)
590 {
591 return CopySpanData (src, target);
592 }
593 template <typename FROM_T, size_t FROM_E, typename TO_T, size_t TO_E>
594 [[deprecated ("Since Stroika v3.0d12 use CopyBytes")]] constexpr std::span<TO_T, TO_E> CopySpanData_StaticCast (span<FROM_T, FROM_E> src,
595 span<TO_T, TO_E> target)
596 {
597 return CopySpanData (ConstSpan (src), target);
598 }
599
600}
bool Intersects(const set< T > &s1, const set< T > &s2)
constexpr strong_ordering CompareResultNormalizer(FROM_INT_TYPE f)
Definition Compare.inl:226