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