ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
boxing/placeholder.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header-file is part of module \alib_boxing of the \aliblong.
4///
5/// Copyright 2013-2026 A-Worx GmbH, Germany.
6/// Published under #"mainpage_license".
7//==================================================================================================
8ALIB_EXPORT namespace alib { namespace boxing { namespace detail {
9
10
11/// Inner struct providing a pointer and a length.<br>
12/// A \c constexpr constructor is given (not shown in documentation).
14 const void* P1; ///< The first pointer.
15 const void* P2; ///< The second pointer.
16
17 // constexpr constructors
18 #if !DOXYGEN
19 constexpr PointerPair( const void* p1) : P1(p1), P2(nullptr) {}
20 constexpr PointerPair( const void* p1, const void* p2) : P1(p1), P2(p2) {}
21 #endif
22};
23
24/// Inner struct providing a pointer and a length.<br>
25/// A \c constexpr constructor is given (not shown in documentation).
27 void* P1; ///< The first pointer.
28 void* P2; ///< The second pointer.
29
30 /// Constructor allowing \c constexpr initialization.
31 /// @param p1 The pointer to store.
32 constexpr PointerPairMutable( void* p1) : P1(p1), P2(nullptr) {}
33
34 /// Constructor allowing \c constexpr initialization.
35 /// @param p1 The first value to store.
36 /// @param p2 The second pointer to store.
37 constexpr PointerPairMutable( void* p1, void* p2) : P1(p1), P2(p2) {}
38};
39
40/// Inner struct providing a pointer and a length.<br>
41/// A \c constexpr constructor is given (not shown in documentation).
43 const void* Pointer; ///< The pointer to the array.
44 integer Length; ///< The length of the array.
45
46 /// Constructor allowing \c constexpr initialization.
47 /// @param p The pointer to the array.
48 /// @param l The length of the array.
49 constexpr StructArray( const void* p, integer l) : Pointer( p ), Length ( l ) {}
50};
51
52/// Inner union with various \c constexpr constructors (not shown in documentation) to support
53/// the initialization of the outer union as \c constexpr.
54///
55/// Collects scalar integrals and arrays of those.
57 int8_t Int8 ; ///< 8-bit signed integral.
58 uint8_t UInt8 ; ///< 8-bit unsigned integral.
59 int16_t Int16 ; ///< 16-bit signed integral.
60 uint16_t UInt16 ; ///< 16-bit unsigned integral.
61
62 #if DOXYGEN
63 int32_t Int32 ; ///< 32-bit signed integral. Available only if platform is not of 32-bit.
64 uint32_t UInt32 ; ///< 32-bit unsigned integral. Available only if platform is not of 32-bit.
65 int64_t Int64 ; ///< 64-bit signed integral. Available only if platform is not of 64-bit.
66 uint64_t UInt64 ; ///< 64-bit unsigned integral. Available only if platform is not of 64-bit.
67 #elif ALIB_SIZEOF_INTEGER != 4
68 int32_t Int32 ;
69 uint32_t UInt32 ;
70 #elif ALIB_SIZEOF_INTEGER != 8
71 int64_t Int64 ;
72 uint64_t UInt64 ;
73 #endif
74
75 integer Int ; ///< Signed integral of platform-dependent size.
76 uinteger UInt ; ///< Unsigned integral of platform-dependent size.
77
78 int8_t Array8 [2 * sizeof(void*) / sizeof( int8_t )]; ///< Array of 8-bit signed integrals of length 16 on 64-bit platform, 8 on a 32-bit platform.
79 int16_t Array16 [2 * sizeof(void*) / sizeof( int16_t )]; ///< Array of 16-bit signed integrals of length 8 on 64-bit platform, 4 on a 32-bit platform.
80
81 #if DOXYGEN
82 int32_t Array32 [2 * sizeof(void*) / sizeof( int32_t )]; ///< Array of 32-bit signed integrals of length 4 on a 64-bit platform. Not available on 32-bit platforms.
83 int64_t Array64 [2 * sizeof(void*) / sizeof( int64_t )]; ///< Array of 64-bit signed integrals of length 1 on a 32-bit platform. Not available on 64-bit platforms.
84
85 #elif ALIB_SIZEOF_INTEGER != 4
86 int32_t Array32 [2 * sizeof(void*) / sizeof( int32_t )];
87 #elif ALIB_SIZEOF_INTEGER != 8
88 int64_t Array64 [2 * sizeof(void*) / sizeof( int64_t )];
89 #endif
90
91 integer Array [ 2 ]; ///< Array of 64-bit signed integrals of length two on 64-bit platform, one on a 32-bit platform.
92 uinteger UArray [ 2 ]; ///< Array of 64-bit unsigned integrals of length two on 64-bit platform, one on a 32-bit platform.
93
94
95 /// Constructor allowing \c constexpr initialization.
96 /// @param v1 The first value to store.
97 /// @param v2 The second value to store.
98 constexpr UnionIntegrals ( integer v1, integer v2 ) : Array { v1, v2 } {}
99
100 /// Constructor allowing \c constexpr initialization.
101 /// @param value The value to store.
102 constexpr UnionIntegrals ( int8_t value ) : Int8 { value } {}
103
104 /// Constructor allowing \c constexpr initialization.
105 /// @param value The value to store.
106 constexpr UnionIntegrals ( uint8_t value ) : UInt8 { value } {}
107
108 /// Constructor allowing \c constexpr initialization.
109 /// @param value The value to store.
110 constexpr UnionIntegrals ( int16_t value ) : Int16 { value } {}
111
112 /// Constructor allowing \c constexpr initialization.
113 /// @param value The value to store.
114 constexpr UnionIntegrals ( uint16_t value ) : UInt16 { value } {}
115 #if ALIB_SIZEOF_INTEGER != 4
116
117 /// Constructor allowing \c constexpr initialization.
118 /// @param value The value to store.
119 constexpr UnionIntegrals ( int32_t value ) : Int32 { value } {}
120
121 /// Constructor allowing \c constexpr initialization.
122 /// @param value The value to store.
123 constexpr UnionIntegrals ( uint32_t value ) : UInt32 { value } {}
124 #elif ALIB_SIZEOF_INTEGER != 8
125 constexpr UnionIntegrals ( int64_t value ) : Int64 { value } {}
126 constexpr UnionIntegrals ( uint64_t value ) : UInt64 { value } {}
127 #endif
128 /// Constructor allowing \c constexpr initialization.
129 /// @param value The value to store.
130 constexpr UnionIntegrals ( integer value ) : Int { value } {}
131
132 /// Constructor allowing \c constexpr initialization.
133 /// @param value The value to store.
134 constexpr UnionIntegrals ( uinteger value ) : UInt { value } {}
135
136
137 #if ALIB_SIZEOF_INTGAP == 2
138 /// Constructor allowing \c constexpr initialization.
139 /// @param value The value to store.
140 constexpr UnionIntegrals ( intGap_t value ) : Int16 { value } {}
141 /// Constructor allowing \c constexpr initialization.
142 /// @param value The value to store.
143 constexpr UnionIntegrals ( uintGap_t value ) : UInt16 { value } {}
144 #elif ALIB_SIZEOF_INTGAP == 4
145 #if ALIB_SIZEOF_INTEGER != 4
146 /// Constructor allowing \c constexpr initialization.
147 /// @param value The value to store.
148 constexpr UnionIntegrals ( intGap_t value ) : Int32 { value } {}
149 /// Constructor allowing \c constexpr initialization.
150 /// @param value The value to store.
151 constexpr UnionIntegrals ( uintGap_t value ) : UInt32 { value } {}
152 #else
153 /// Constructor allowing \c constexpr initialization.
154 /// @param value The value to store.
155 constexpr UnionIntegrals ( intGap_t value ) : Int { value } {}
156 /// Constructor allowing \c constexpr initialization.
157 /// @param value The value to store.
158 constexpr UnionIntegrals ( uintGap_t value ) : UInt { value } {}
159 #endif
160 #elif ALIB_SIZEOF_INTGAP == 8
161 #if ALIB_SIZEOF_INTEGER != 8
162 constexpr UnionIntegrals ( intGap_t value ) : Int64 { value } {}
163 constexpr UnionIntegrals ( uintGap_t value ) : UInt64 { value } {}
164 #else
165 /// Constructor allowing \c constexpr initialization.
166 /// @param value The value to store.
167 constexpr UnionIntegrals ( intGap_t value ) : Int { value } {}
168 /// Constructor allowing \c constexpr initialization.
169 /// @param value The value to store.
170 constexpr UnionIntegrals ( uintGap_t value ) : UInt { value } {}
171 #endif
172 #else
173 #error "ALIB_SIZEOF_INTGAP not matched. Supported sizes are 2, 4 and 8."
174 #endif
175};
176
177/// Inner union with various \c constexpr constructors (not shown in documentation) to support
178/// the initialization of the outer union as \c constexpr.
179///
180/// Collects scalar floating points and arrays of those.
182 float Float ; ///< A \c float value.
183 double Double ; ///< A \c double value.
184
185 /// Array of \c float. The Length is usually four on 64-bit platform, two on a 32-bit platform.
186 float FloatArray [2 * sizeof(void*) / sizeof(float )];
187
188 /// Array of \c double. The Length is usually two on 64-bit platform, one on a 32-bit platform.
189 double DoubleArray [2 * sizeof(void*) / sizeof(double )];
190
191 /// Constructor allowing \c constexpr initialization.
192 /// @param value The value to store.
193 constexpr UnionFloatingPoints( float value ) : Float { value } {}
194 /// Constructor allowing \c constexpr initialization.
195 /// @param value The value to store.
196 constexpr UnionFloatingPoints( double value ) : Double { value } {}
197};
198
199
200
201/// Collects byte arrays of each possible size. This is used with overloaded method
202/// #"Placeholder::Write(const TInt)" that leverages C++20 function \c std::bit_cast to
203/// perform \c constexpr boxing.<br>
204/// Consequently, this union does not provide \c constexpr constructors.
206 std::array<char, 1> C1; ///< 1 bytes.
207 std::array<char, 2> C2; ///< 2 bytes.
208 std::array<char, 3> C3; ///< 3 bytes.
209 std::array<char, 4> C4; ///< 4 bytes.
210 std::array<char, 5> C5; ///< 5 bytes.
211 std::array<char, 6> C6; ///< 6 bytes.
212 std::array<char, 7> C7; ///< 7 bytes.
213 std::array<char, 8> C8; ///< 8 bytes.
214 #if ALIB_SIZEOF_INTEGER == 8
215 std::array<char, 9> C9; ///< 9 bytes.
216 std::array<char, 10> C10; ///< 10 bytes.
217 std::array<char, 11> C11; ///< 11 bytes.
218 std::array<char, 12> C12; ///< 12 bytes.
219 std::array<char, 13> C13; ///< 13 bytes.
220 std::array<char, 14> C14; ///< 14 bytes.
221 std::array<char, 15> C15; ///< 15 bytes.
222 std::array<char, 16> C16; ///< 16 bytes.
223 #endif
224};
225
226
227} // namespace alib::boxing[::detail]
228
229//==================================================================================================
230/// A #"Box::data;protected member" of this union is contained in class
231/// #"Box" to store information on a boxed object.
232/// This member is passed as an argument to static methods #"%BoxTraits::Write" and
233/// #"%BoxTraits::Read" of type-traits struct #"BoxTraits", which implement boxing and unboxing.
234///
235/// This union declares different inner structs and unions and contains one corresponding member of
236/// each. This sorts the union fields into different groups, which his also helpful when
237/// debugging instances of type box.
238///
239/// The overall size of this union is two times the size of \c std::size_t, hence 16 bytes on a
240/// 64-bit and 8 bytes on a 32-bit system.
241///
242/// Virtually any sort of data might be written into the union. With non-injective boxing, what
243/// means that two or more types are boxed to the same target type, the format that type uses
244/// has to be implemented by all #"%BoxTraits::Write" and #"%BoxTraits::Read" methods of any
245/// specialization of #"BoxTraits". Otherwise, undefined behavior occurs.
246///
247/// This type offers two sets of templated overloaded methods, named \b Write and \b Read.
248/// In addition to be overloaded, the methods use C++20 concepts to be selected by the
249/// compiler only for certain template types.<br>
250/// The methods cover boxing and unboxing of the most frequent types like
251/// - fundamental types,
252/// - "fitting" value types that are trivially copyable,
253/// - "fitting" value types that are not trivially copyable (not \c constexpr)
254/// - pointers and
255/// - arrays.
256///
257/// For these types, the default implementation of #"%BoxTraits::Write" and #"%BoxTraits::Read", as
258/// given, for example, with macro #"ALIB_BOXING_CUSTOMIZE_TYPE_MAPPING" often is all that is
259/// needed.
260///
261/// ####"Custom" Boxing####
262/// Custom implementations of boxing and unboxing may read from and write to the union data
263/// directly.<br>
264/// In this case, a "continuous" use of the available data is suggested. At least, gaps
265/// in writing should be initialized with a value (e.g., \c 0). The rationale for this is
266/// that the default implementations of box-functions #"FHashcode" and
267/// #"FEquals" use only the first \e N relevant bytes. If now, gaps are not written,
268/// they contain "random" data, what would cause a failure of these default functions.<br>
269///
270/// By the same token, if the customization of a non-array type writes a different length than
271/// C++ operator \c sizeof reports for the mapped type, then also #"SizeTraits" has
272/// to be specialized for that type, so that the method
273/// #"Box::GetPlaceholderUsageLength" reports the right value.
274/// Note that furthermore method #".Clear", which is used when boxing \e nulled pointers, only
275/// clears as much data in this struct as reported by #"SizeTraits".
276///
277/// ####"Constexpr" Boxing####
278/// While this library defines <c>constexpr</c>-boxing for all fundamental types and for most
279/// library types of other \alibmods, still such customization is considered "expert use" as the
280/// gain to do it for custom types is marginal. The biggest benefit of \c constexpr boxing,
281/// lies in the fact that - if also a customized VTable is provided - such types can be located
282/// in global and static instances of class #"%Box".
283///
284/// \see
285/// - Chapter #"alib_boxing_customizing" of the Programmer's Manual of
286/// \alib_boxing_nl.<br>
287/// - Struct #"BoxTraits" and expression #"SizeTraits".
288//==================================================================================================
290 detail::PointerPair PointerPair; ///< Collection of two \c const \c void pointers.
291 detail::PointerPairMutable PointerPairMutable; ///< Collection of two \c void pointers.
292 detail::StructArray Array; ///< Used when storing C++ arrays.
293 detail::UnionIntegrals Integrals; ///< Collection of integrals of different sizes, placed next to each other.
294 detail::UnionFloatingPoints FloatingPoints; ///< Collection of floating points of different sizes.
295 detail::UnionBytes Bytes; ///< Byte arrays of different length.
296 void* VoidP; ///< Just a void pointer.
297 #if ALIB_DEBUG
298 character* Debugger_String; ///< This union field was inserted only for debug display.
299 integer Debugger_Integral; ///< This union field was inserted only for debug display.
300 #endif
301
302 /// Default constructor. Leaves everything uninitialized.
303 constexpr Placeholder() {}
304
305 // constexpr constructors
306 #if !DOXYGEN
307
308 // Pointer construction
309 template<typename TPointer>
310 constexpr Placeholder( const TPointer* p ) : PointerPair( p ) {}
311
312 template<typename TP1, typename TP2>
313 constexpr Placeholder( const TP1* p1, const TP2* p2 ) : PointerPair( p1, p2 ) {}
314
315 // Integral construction
316 template<typename TI>
317 requires std::is_integral_v<TI>
318 constexpr Placeholder( TI value ) : Integrals ( value ) {}
319
320 template<typename TI1, typename TI2>
321 requires (std::is_integral_v<TI1> && std::is_integral_v<TI2> )
322 constexpr Placeholder( TI1 word1, TI2 word2 ) : Integrals( word1, word2 ) {}
323
324 // Float construction
325 constexpr Placeholder( float value ) : FloatingPoints ( value ) {}
326 constexpr Placeholder( double value ) : FloatingPoints ( value ) {}
327
328 // Array construction
329 template<typename TArray, typename TIntegral>
330 requires std::is_integral_v<TIntegral>
331 constexpr Placeholder( const TArray* tpointer, TIntegral length )
332 : Array( tpointer, integer(length)) {}
333
334 #endif
335
336 /// Returns a pointer of type \c void*.
337 /// @return The pointer stored.
338 constexpr const void* GetVoidPointer() const { return VoidP; }
339
340 /// Returns a pointer of type \p{TReturn}.
341 /// @tparam TReturn The requested pointer type
342 /// @return The pointer stored.
343 template<typename TReturn>
344 TReturn* GetPointer () const { return reinterpret_cast<TReturn*>(PointerPairMutable.P1); }
345
346 /// Returns a pointer of type \p{TReturn}.
347 /// @tparam TReturn The requested pointer type
348 /// @return The pointer stored.
349 template<typename TReturn>
350 TReturn* GetPointer2 () const { return reinterpret_cast<TReturn*>(PointerPairMutable.P2); }
351
352 /// Sets a pointer of type \c char*.
353 /// @param value The value to set.
354 constexpr void SetPointer ( void* value ) { PointerPair.P1= value; }
355
356 /// Returns the length of a stored array (the second word stored).
357 /// @return The length stored.
358 constexpr integer GetLength () const { return Array.Length; }
359
360 /// Clears this box data.
361 ///
362 /// It has to be ensured that all the memory used by a mapped type is cleared.
363 /// For example, the default implementations of box-functions #"FHashcode" and
364 /// #"FEquals" are using the relevant bytes of this placeholder, and those must
365 /// not be of random value.
366 ///
367 /// For efficiency reasons, the rest should not be cleared.
368 ///
369 /// @tparam UsageLength The number of bytes to clear.
370 template<unsigned UsageLength>
371 constexpr void Clear() {
372 static_assert( UsageLength > 0 && ( UsageLength <= 2 * sizeof(void*) ),
373 "Invalid usage length given" );
374
375 PointerPair.P1= nullptr;
376 if constexpr( UsageLength > sizeof(void*) )
377 PointerPair.P2= nullptr;
378 }
379
380 //################################################################################################
381 //############################################# Boxing ###########################################
382 //################################################################################################
383
384 /// This version of the overloaded method writes integral values.
385 /// @tparam TIntegral The integral type to store.
386 /// @param value The value to store.
387 template<typename TIntegral>
388 requires( std::is_integral_v<TIntegral> )
389 constexpr void Write( const TIntegral& value ) {
390
391 if constexpr( std::is_signed_v<TIntegral>) {
392
393 if constexpr( sizeof(TIntegral) == 1 ) Integrals.Int8 = int8_t (value);
394 else if constexpr( sizeof(TIntegral) == 2 ) Integrals.Int16 = int16_t(value);
395 #if ALIB_SIZEOF_INTEGER == 4
396 else if constexpr( sizeof(TIntegral) == 4 ) Integrals.Int = integer(value);
397 else if constexpr( sizeof(TIntegral) == 8 ) Integrals.Int64 = int64_t(value);
398 #else
399 else if constexpr( sizeof(TIntegral) == 4 ) Integrals.Int32 = int32_t(value);
400 else if constexpr( sizeof(TIntegral) == 8 ) Integrals.Int = integer(value);
401 #endif
402 } else {
403 if constexpr( sizeof(TIntegral) == 1 ) Integrals.UInt8 = uint8_t (value);
404 else if constexpr( sizeof(TIntegral) == 2 ) Integrals.UInt16= uint16_t(value);
405 #if ALIB_SIZEOF_INTEGER == 4
406 else if constexpr( sizeof(TIntegral) == 4 ) Integrals.UInt = uinteger(value);
407 else if constexpr( sizeof(TIntegral) == 8 ) Integrals.UInt64= uint64_t(value);
408 #else
409 else if constexpr( sizeof(TIntegral) == 4 ) Integrals.UInt32= uint32_t(value);
410 else if constexpr( sizeof(TIntegral) == 8 ) Integrals.UInt = uinteger(value);
411 #endif
412 } }
413
414 /// This version of the overloaded method writes floating-point values.
415 /// @tparam TFloat The integral type to store.
416 /// @param value The value to store.
417 template<typename TFloat>
418 requires( std::is_floating_point_v<TFloat> )
419 constexpr void Write( const TFloat& value ) {
420 if constexpr( std::same_as<TFloat, float > ) FloatingPoints.Float = value;
421 else if constexpr( std::same_as<TFloat, double> ) FloatingPoints.Double= value;
422 }
423
424 /// This version of the overloaded method requires type \p{TTrivial} to:
425 /// - be trivially copyable,
426 /// - be a non-pointer type,
427 /// - that it fits into the placeholder which is of <c>2 * sizeof(void*)</c>, and
428 /// - is not integral (handled with other overload).
429 ///
430 /// This version copies the value using \c std::bit_cast.
431 /// This way, this method may be used with \c constexpr use cases that enable
432 /// #"alib_boxing_more_opt_constexpr;constant boxes", which in seldom cases may be required.
433 /// @tparam TTrivial The mapped type to store.
434 /// @param value The value of type \p{TTrivial} to store.
435 template<typename TTrivial>
436 requires( std::is_trivially_copyable_v<TTrivial>
437 && (sizeof(TTrivial) <= 2 * sizeof(void*))
438 && !std::is_pointer_v<TTrivial>
439 && !std::is_integral_v<TTrivial>
440 && !std::is_floating_point_v<TTrivial> )
441 constexpr void Write( const TTrivial& value ) {
442
443 if constexpr( sizeof(TTrivial) == 1 ) Bytes.C1 = std::bit_cast<std::array<char, 1>>( value );
444 else if constexpr( sizeof(TTrivial) == 2 ) Bytes.C2 = std::bit_cast<std::array<char, 2>>( value );
445 else if constexpr( sizeof(TTrivial) == 3 ) Bytes.C3 = std::bit_cast<std::array<char, 3>>( value );
446 else if constexpr( sizeof(TTrivial) == 4 ) Bytes.C4 = std::bit_cast<std::array<char, 4>>( value );
447 else if constexpr( sizeof(TTrivial) == 5 ) Bytes.C5 = std::bit_cast<std::array<char, 5>>( value );
448 else if constexpr( sizeof(TTrivial) == 6 ) Bytes.C6 = std::bit_cast<std::array<char, 6>>( value );
449 else if constexpr( sizeof(TTrivial) == 7 ) Bytes.C7 = std::bit_cast<std::array<char, 7>>( value );
450 else if constexpr( sizeof(TTrivial) == 8 ) Bytes.C8 = std::bit_cast<std::array<char, 8>>( value );
451 #if ALIB_SIZEOF_INTEGER == 8
452 else if constexpr( sizeof(TTrivial) == 9 ) Bytes.C9 = std::bit_cast<std::array<char, 9>>( value );
453 else if constexpr( sizeof(TTrivial) == 10 ) Bytes.C10= std::bit_cast<std::array<char, 10>>( value );
454 else if constexpr( sizeof(TTrivial) == 11 ) Bytes.C11= std::bit_cast<std::array<char, 11>>( value );
455 else if constexpr( sizeof(TTrivial) == 12 ) Bytes.C12= std::bit_cast<std::array<char, 12>>( value );
456 else if constexpr( sizeof(TTrivial) == 13 ) Bytes.C13= std::bit_cast<std::array<char, 13>>( value );
457 else if constexpr( sizeof(TTrivial) == 14 ) Bytes.C14= std::bit_cast<std::array<char, 14>>( value );
458 else if constexpr( sizeof(TTrivial) == 15 ) Bytes.C15= std::bit_cast<std::array<char, 15>>( value );
459 else if constexpr( sizeof(TTrivial) == 16 ) Bytes.C16= std::bit_cast<std::array<char, 16>>( value );
460 #endif
461 }
462
463 /// Writes two values of arbitrary type into the placeholder.
464 /// The method requires that:
465 /// - Types \p{T1} and \p{T2} are trivially copyable
466 /// - The types are no pointers
467 /// - The sum of the types' sizes fit into the placeholder which is of <c>2 * sizeof(void*)</c>.
468 ///
469 /// This version packs the two given values with no gap into a tuple and then writes that
470 /// tuple using \c std::bit_cast. This way, this method may be used with \c constexpr
471 /// use cases that enable #"alib_boxing_more_opt_constexpr;constant boxes", which in
472 /// seldom cases may be required. Furthermore, this way, values boxed like this fulfil one
473 /// requirement to produce reliable #"alib_boxing_customizing_placeholder;hash values".
474 ///
475 /// @tparam T1 The type of the first value to store.
476 /// @tparam T2 The type of the second value to store.
477 /// @param v1 The first value to store.
478 /// @param v2 The second value to store.
479 template<typename T1, typename T2>
480 requires( std::is_trivially_copyable_v<T1> && !std::is_pointer_v<T1>
481 && std::is_trivially_copyable_v<T2> && !std::is_pointer_v<T2>
482 && ( sizeof(T1) + sizeof(T2) <= 2 * sizeof(void*) )
483 )
484 constexpr void Write( const T1& v1, const T2& v2 ) {
485
486 // Force the struct to be packed (i.e. no padding in between)
487 #pragma pack(push, 1)
488 struct PackedTuple {
489 T1 first;
490 T2 second;
491 };
492 #pragma pack(pop)
493
494 static_assert(sizeof(PackedTuple) == sizeof(T1) + sizeof(T2),
495 "PackedTuple has unexpected padding!");
496
497 // Pack the two values into our packed tuple.
498 PackedTuple value { v1, v2 };
499
500 if constexpr( sizeof(PackedTuple) == 1 ) Bytes.C1 = std::bit_cast<std::array<char, 1>>( value );
501 else if constexpr( sizeof(PackedTuple) == 2 ) Bytes.C2 = std::bit_cast<std::array<char, 2>>( value );
502 else if constexpr( sizeof(PackedTuple) == 3 ) Bytes.C3 = std::bit_cast<std::array<char, 3>>( value );
503 else if constexpr( sizeof(PackedTuple) == 4 ) Bytes.C4 = std::bit_cast<std::array<char, 4>>( value );
504 else if constexpr( sizeof(PackedTuple) == 5 ) Bytes.C5 = std::bit_cast<std::array<char, 5>>( value );
505 else if constexpr( sizeof(PackedTuple) == 6 ) Bytes.C6 = std::bit_cast<std::array<char, 6>>( value );
506 else if constexpr( sizeof(PackedTuple) == 7 ) Bytes.C7 = std::bit_cast<std::array<char, 7>>( value );
507 else if constexpr( sizeof(PackedTuple) == 8 ) Bytes.C8 = std::bit_cast<std::array<char, 8>>( value );
508 #if ALIB_SIZEOF_INTEGER == 8
509 else if constexpr( sizeof(PackedTuple) == 9 ) Bytes.C9 = std::bit_cast<std::array<char, 9>>( value );
510 else if constexpr( sizeof(PackedTuple) == 10 ) Bytes.C10= std::bit_cast<std::array<char, 10>>( value );
511 else if constexpr( sizeof(PackedTuple) == 11 ) Bytes.C11= std::bit_cast<std::array<char, 11>>( value );
512 else if constexpr( sizeof(PackedTuple) == 12 ) Bytes.C12= std::bit_cast<std::array<char, 12>>( value );
513 else if constexpr( sizeof(PackedTuple) == 13 ) Bytes.C13= std::bit_cast<std::array<char, 13>>( value );
514 else if constexpr( sizeof(PackedTuple) == 14 ) Bytes.C14= std::bit_cast<std::array<char, 14>>( value );
515 else if constexpr( sizeof(PackedTuple) == 15 ) Bytes.C15= std::bit_cast<std::array<char, 15>>( value );
516 else if constexpr( sizeof(PackedTuple) == 16 ) Bytes.C16= std::bit_cast<std::array<char, 16>>( value );
517 #endif
518 }
519
520 /// This version of the overloaded method is used for boxing C++ pointer types.
521 ///
522 /// Note that for unboxing custom types from C++ array types, a custom implementation of
523 /// #"BoxTraits::Read" is needed. Such an implementation reads the pointer and length
524 /// directly from this struct. In other words, there is no overloaded method #"%.Read"
525 /// available for array types.
526 ///
527 /// @tparam TPointer The pointer type.
528 /// @param pointer The pointer to store.
529 template<typename TPointer>
530 constexpr void Write( const TPointer* pointer) { *this= Placeholder(pointer); }
531
532 /// This version of the overloaded method is used for boxing C++ array types.
533 /// The type and length of the array is stored in the field #".Array" of the data union.
534 ///
535 /// Note that for unboxing custom types from C++ array types, a custom implementation of
536 /// #"BoxTraits::Read" is needed. Such an implementation reads the pointer and length
537 /// directly from this struct.
538 /// (I.e. there is no overloaded method #"%.Read" available for arrays.)
539 ///
540 /// @tparam TArray The pointer type.
541 /// @param pointer The pointer to store.
542 /// @param length The array's length to store.
543 template<typename TArray>
544 constexpr void Write( const TArray* pointer, integer length )
545 { *this= Placeholder(pointer, length); }
546
547 /// This version of the overloaded method is used for boxing two pointer types.
548 /// @tparam TP1 The type of the first pointer.
549 /// @tparam TP2 The type of the second pointer.
550 /// @param p1 The first pointer to store.
551 /// @param p2 The second pointer to store.
552 template<typename TP1, typename TP2>
553 constexpr void Write( const TP1* p1, const TP2* p2 ) { *this= Placeholder(p1, p2); }
554
555 /// This version of the overloaded method is selected when the type \p{TPointer} is not
556 /// trivially copyable, still fits into the placeholder of size <c>2 * sizeof(void*)</c>, and
557 /// furthermore does not directly match other overloaded versions.
558 ///
559 /// The copying is performed using \c memcpy.
560 /// This is necessary to avoid de-referencing type-punned pointers which would
561 /// break the strict-aliasing rule when compiling the code with higher optimization levels.
562 /// Note that modern compilers like GCC usually optimize the invocation of \c memcpy out.<br>
563 ///
564 /// This version is \b not \c constexpr and thus may not be used to create
565 /// #"alib_boxing_more_opt_constexpr;constant boxes", which in
566 /// seldom cases may be required.
567 ///
568 /// @tparam TPointer The type of the value to store.
569 /// @param value The value to store.
570 template<typename TPointer>
571 requires( !std::is_trivially_copyable_v<TPointer>
572 && (sizeof(TPointer) <= 2 * sizeof(void*)) )
573 void Write( const TPointer& value )
574 { std::memcpy( &PointerPair.P1, &value, sizeof(TPointer) ); }
575
576 //################################################################################################
577 //############################################ Unboxing ##########################################
578 //################################################################################################
579// Doxygen (V1.15) would create identical entries in its tag-file, so those are not reachable.
580// On the other hand, it complains if the methods are not doxed. So, we hide them from doxygen.
581#if DOXYGEN
582 /// Reads a value of type \p{TMapped} from this placeholder.<br>
583 /// Internally, this method is implemented using various variants which are selected using
584 /// the keyword \c requires.
585 ///
586 /// @tparam TMapped The value type to unbox.
587 /// @return The value stored.
588 template<typename TMapped>
589 requires( !( std::is_trivially_copyable_v<TMapped>
590 && (sizeof(TMapped) <= 2 * sizeof(void*)) )
591 && !std::is_pointer_v<TMapped>
592 )
593 TMapped Read();
594#else
595
596 template<typename TMapped>
597 requires( !( std::is_trivially_copyable_v<TMapped>
598 && (sizeof(TMapped) <= 2 * sizeof(void*)) )
599 && !std::is_pointer_v<TMapped>
600 )
601 TMapped Read() const { return *reinterpret_cast<const TMapped*>( this ); }
602
603 template<typename TPointer>
604 requires std::is_pointer_v<TPointer>
605 constexpr TPointer Read() const {
606 if constexpr (std::same_as<TPointer, void*>)
607 return VoidP;
608 return static_cast<TPointer>( VoidP );
609 }
610
611 template<typename TIntegral>
612 requires( std::is_integral_v<TIntegral> )
613 constexpr TIntegral Read() const {
614
615 if constexpr( std::is_signed_v<TIntegral>) {
616
617 if constexpr( sizeof(TIntegral) == 1 ) return TIntegral(Integrals.Int8 );
618 else if constexpr( sizeof(TIntegral) == 2 ) return TIntegral(Integrals.Int16);
619 #if ALIB_SIZEOF_INTEGER == 4
620 else if constexpr( sizeof(TIntegral) == 4 ) return TIntegral(Integrals.Int );
621 else if constexpr( sizeof(TIntegral) == 8 ) return TIntegral(Integrals.Int64);
622 #else
623 else if constexpr( sizeof(TIntegral) == 4 ) return TIntegral(Integrals.Int32);
624 else if constexpr( sizeof(TIntegral) == 8 ) return TIntegral(Integrals.Int );
625 #endif
626 } else {
627 if constexpr( sizeof(TIntegral) == 1 ) return TIntegral(Integrals.UInt8 );
628 else if constexpr( sizeof(TIntegral) == 2 ) return TIntegral(Integrals.UInt16);
629 #if ALIB_SIZEOF_INTEGER == 4
630 else if constexpr( sizeof(TIntegral) == 4 ) return TIntegral(Integrals.UInt );
631 else if constexpr( sizeof(TIntegral) == 8 ) return TIntegral(Integrals.UInt64);
632 #else
633 else if constexpr( sizeof(TIntegral) == 4 ) return TIntegral(Integrals.UInt32);
634 else if constexpr( sizeof(TIntegral) == 8 ) return TIntegral(Integrals.UInt );
635 #endif
636 } }
637
638 template<typename TFloat>
639 requires( std::is_floating_point_v<TFloat> )
640 constexpr TFloat Read() const {
641 if constexpr( std::same_as<TFloat, float > ) return TFloat(FloatingPoints.Float );
642 else if constexpr( std::same_as<TFloat, double> ) return TFloat(FloatingPoints.Double);
643 }
644
645 template<typename TTrivial>
646 requires( std::is_trivially_copyable_v<TTrivial>
647 && (sizeof(TTrivial) <= 2 * sizeof(void*))
648 && !std::is_pointer_v<TTrivial>
649 && !std::is_integral_v<TTrivial>
650 && !std::is_floating_point_v<TTrivial>
651 )
652 constexpr TTrivial Read() const {
653
654 if constexpr( sizeof(TTrivial) == 1 ) return std::bit_cast<TTrivial>(Bytes.C1);
655 else if constexpr( sizeof(TTrivial) == 2 ) return std::bit_cast<TTrivial>(Bytes.C2);
656 else if constexpr( sizeof(TTrivial) == 3 ) return std::bit_cast<TTrivial>(Bytes.C3);
657 else if constexpr( sizeof(TTrivial) == 4 ) return std::bit_cast<TTrivial>(Bytes.C4);
658 else if constexpr( sizeof(TTrivial) == 5 ) return std::bit_cast<TTrivial>(Bytes.C5);
659 else if constexpr( sizeof(TTrivial) == 6 ) return std::bit_cast<TTrivial>(Bytes.C6);
660 else if constexpr( sizeof(TTrivial) == 7 ) return std::bit_cast<TTrivial>(Bytes.C7);
661 else if constexpr( sizeof(TTrivial) == 8 ) return std::bit_cast<TTrivial>(Bytes.C8);
662 #if ALIB_SIZEOF_INTEGER == 8
663 else if constexpr( sizeof(TTrivial) == 9 ) return std::bit_cast<TTrivial>(Bytes.C9 );
664 else if constexpr( sizeof(TTrivial) == 10 ) return std::bit_cast<TTrivial>(Bytes.C10);
665 else if constexpr( sizeof(TTrivial) == 11 ) return std::bit_cast<TTrivial>(Bytes.C11);
666 else if constexpr( sizeof(TTrivial) == 12 ) return std::bit_cast<TTrivial>(Bytes.C12);
667 else if constexpr( sizeof(TTrivial) == 13 ) return std::bit_cast<TTrivial>(Bytes.C13);
668 else if constexpr( sizeof(TTrivial) == 14 ) return std::bit_cast<TTrivial>(Bytes.C14);
669 else if constexpr( sizeof(TTrivial) == 15 ) return std::bit_cast<TTrivial>(Bytes.C15);
670 else if constexpr( sizeof(TTrivial) == 16 ) return std::bit_cast<TTrivial>(Bytes.C16);
671 #endif
672 }
673#endif
674
675 /// Templated method that reads two trivially copyable types that jointly fit into the
676 /// placeholder.
677 /// @tparam TTrivial1 The type of the first value to read from this placeholder.
678 /// @tparam TTrivial2 The type of the second value to read from this placeholder.
679 /// @param v1 Output parameter. Receives the first value.
680 /// @param v2 Output parameter. Receives the second value.
681 template<typename TTrivial1, typename TTrivial2>
682 requires( std::is_trivially_copyable_v<TTrivial1> && !std::is_pointer_v<TTrivial1>
683 && std::is_trivially_copyable_v<TTrivial2> && !std::is_pointer_v<TTrivial2>
684 && ( sizeof(TTrivial1) + sizeof(TTrivial2) <= 2 * sizeof(void*) )
685 )
686 constexpr void Read(TTrivial1& v1, TTrivial2& v2) const {
687
688 // Ensure the packed structure has no gaps.
689 #pragma pack(push, 1)
690 struct PT {
691 TTrivial1 v1;
692 TTrivial2 v2;
693 };
694 #pragma pack(pop)
695
696 static_assert(sizeof(PT) == sizeof(TTrivial1) + sizeof(TTrivial2),
697 "PackedTuple has unexpected padding!");
698
699 // Depending on the size of PackedTuple, bit_cast from the corresponding member
700 // of Bytes to our packed structure, then assign the values.
701 if constexpr( sizeof(PT) == 1 ) { const auto pt = std::bit_cast<PT>(Bytes.C1); v1 = pt.v1; v2 = pt.v2; }
702 else if constexpr( sizeof(PT) == 2 ) { const auto pt = std::bit_cast<PT>(Bytes.C2); v1 = pt.v1; v2 = pt.v2; }
703 else if constexpr( sizeof(PT) == 3 ) { const auto pt = std::bit_cast<PT>(Bytes.C3); v1 = pt.v1; v2 = pt.v2; }
704 else if constexpr( sizeof(PT) == 4 ) { const auto pt = std::bit_cast<PT>(Bytes.C4); v1 = pt.v1; v2 = pt.v2; }
705 else if constexpr( sizeof(PT) == 5 ) { const auto pt = std::bit_cast<PT>(Bytes.C5); v1 = pt.v1; v2 = pt.v2; }
706 else if constexpr( sizeof(PT) == 6 ) { const auto pt = std::bit_cast<PT>(Bytes.C6); v1 = pt.v1; v2 = pt.v2; }
707 else if constexpr( sizeof(PT) == 7 ) { const auto pt = std::bit_cast<PT>(Bytes.C7); v1 = pt.v1; v2 = pt.v2; }
708 else if constexpr( sizeof(PT) == 8 ) { const auto pt = std::bit_cast<PT>(Bytes.C8); v1 = pt.v1; v2 = pt.v2; }
709 #if ALIB_SIZEOF_INTEGER > 4
710 else if constexpr( sizeof(PT) == 9 ) { const auto pt = std::bit_cast<PT>(Bytes.C9); v1 = pt.v1; v2 = pt.v2; }
711 else if constexpr( sizeof(PT) == 10 ) { const auto pt = std::bit_cast<PT>(Bytes.C10); v1 = pt.v1; v2 = pt.v2; }
712 else if constexpr( sizeof(PT) == 11 ) { const auto pt = std::bit_cast<PT>(Bytes.C11); v1 = pt.v1; v2 = pt.v2; }
713 else if constexpr( sizeof(PT) == 12 ) { const auto pt = std::bit_cast<PT>(Bytes.C12); v1 = pt.v1; v2 = pt.v2; }
714 else if constexpr( sizeof(PT) == 13 ) { const auto pt = std::bit_cast<PT>(Bytes.C13); v1 = pt.v1; v2 = pt.v2; }
715 else if constexpr( sizeof(PT) == 14 ) { const auto pt = std::bit_cast<PT>(Bytes.C14); v1 = pt.v1; v2 = pt.v2; }
716 else if constexpr( sizeof(PT) == 15 ) { const auto pt = std::bit_cast<PT>(Bytes.C15); v1 = pt.v1; v2 = pt.v2; }
717 else if constexpr( sizeof(PT) == 16 ) { const auto pt = std::bit_cast<PT>(Bytes.C16); v1 = pt.v1; v2 = pt.v2; }
718 #endif
719 }
720}; // union Placeholder
721
722static_assert( sizeof(Placeholder) == 2 * sizeof(std::size_t),
723 "Size of boxing::Placeholder is not two times the size of 'size_t'. "
724 "Compilation platform not supported." );
725
726
727//==================================================================================================
728/// This is a simple helper type that forms a pair of two values. It is useful when the boxing of
729/// such a pair is wanted. The benefit over using <c>std::pair</c> is, that this struct does not
730/// provide any extras but the plain public values, and thus it is trivially copyable in the case
731/// that both types are.
732/// @tparam T1 The type of the first value.
733/// @tparam T2 The type of the second first value.
734//==================================================================================================
735template <typename T1, typename T2>
736requires ( std::is_trivially_copyable_v<T1>
737 && std::is_trivially_copyable_v<T2>
738 && ( sizeof(T1) + sizeof(T2) <= 2 * sizeof(void*) )
739 )
740struct Pair {
741 T1 First; ///< The first value.
742 T2 Second; ///< The second value.
743};
744
745//==================================================================================================
746/// This is a simple helper function that constructs a #"Pair".
747/// @tparam T1 The type of the first value. Deduced by the compiler.
748/// @tparam T2 The type of the second first value. Deduced by the compiler.
749/// @param t1 The first value.
750/// @param t2 The second value.
751/// @return The pair of deduced types.
752//==================================================================================================
753template <typename T1, typename T2>
754requires ( std::is_trivially_copyable_v<T1>
755 && std::is_trivially_copyable_v<T2>
756 && ( sizeof(T1) + sizeof(T2) <= 2 * sizeof(void*) )
757 )
758constexpr Pair<T1, T2> MakePair( const T1& t1, const T2& t2) { return Pair<T1, T2>{t1, t2}; }
759
760}
761
762/// Type alias in namespace #"%alib".
763template <typename T, typename U>
765
766} // namespace [alib::boxing]
#define ALIB_EXPORT
This namespace implements internals of namespace #"alib::boxing;2".
Definition vtable.cpp:1
DOXYGEN.
Definition box.cpp:17
constexpr Pair< T1, T2 > MakePair(const T1 &t1, const T2 &t2)
Definition alox.cpp:14
lang::intGap_t intGap_t
Type alias in namespace #"%alib".
Definition integers.hpp:155
lang::integer integer
Type alias in namespace #"%alib".
Definition integers.hpp:149
lang::uintGap_t uintGap_t
Type alias in namespace #"%alib".
Definition integers.hpp:158
boxing::Pair< T, U > Pair
Type alias in namespace #"%alib".
characters::character character
Type alias in namespace #"%alib".
lang::uinteger uinteger
Type alias in namespace #"%alib".
Definition integers.hpp:152
constexpr PointerPairMutable(void *p1, void *p2)
const void * P1
The first pointer.
const void * P2
The second pointer.
integer Length
The length of the array.
const void * Pointer
The pointer to the array.
constexpr StructArray(const void *p, integer l)
detail::PointerPair PointerPair
Collection of two const void pointers.
detail::UnionFloatingPoints FloatingPoints
Collection of floating points of different sizes.
constexpr void Write(const TTrivial &value)
constexpr integer GetLength() const
integer Debugger_Integral
This union field was inserted only for debug display.
constexpr void Write(const TArray *pointer, integer length)
constexpr void Write(const TIntegral &value)
constexpr void Read(TTrivial1 &v1, TTrivial2 &v2) const
character * Debugger_String
This union field was inserted only for debug display.
detail::StructArray Array
Used when storing C++ arrays.
constexpr Placeholder()
Default constructor. Leaves everything uninitialized.
constexpr void Write(const TPointer *pointer)
detail::UnionIntegrals Integrals
Collection of integrals of different sizes, placed next to each other.
detail::PointerPairMutable PointerPairMutable
Collection of two void pointers.
constexpr void Write(const TFloat &value)
constexpr const void * GetVoidPointer() const
constexpr void Write(const TP1 *p1, const TP2 *p2)
void * VoidP
Just a void pointer.
constexpr void SetPointer(void *value)
constexpr void Write(const T1 &v1, const T2 &v2)
detail::UnionBytes Bytes
Byte arrays of different length.
std::array< char, 3 > C3
3 bytes.
std::array< char, 1 > C1
1 bytes.
std::array< char, 13 > C13
13 bytes.
std::array< char, 9 > C9
9 bytes.
std::array< char, 7 > C7
7 bytes.
std::array< char, 8 > C8
8 bytes.
std::array< char, 14 > C14
14 bytes.
std::array< char, 15 > C15
15 bytes.
std::array< char, 12 > C12
12 bytes.
std::array< char, 6 > C6
6 bytes.
std::array< char, 5 > C5
5 bytes.
std::array< char, 16 > C16
16 bytes.
std::array< char, 10 > C10
10 bytes.
std::array< char, 2 > C2
2 bytes.
std::array< char, 4 > C4
4 bytes.
std::array< char, 11 > C11
11 bytes.
float FloatArray[2 *sizeof(void *)/sizeof(float)]
Array of float. The Length is usually four on 64-bit platform, two on a 32-bit platform.
double DoubleArray[2 *sizeof(void *)/sizeof(double)]
Array of double. The Length is usually two on 64-bit platform, one on a 32-bit platform.
uint64_t UInt64
64-bit unsigned integral. Available only if platform is not of 64-bit.
int32_t Int32
32-bit signed integral. Available only if platform is not of 32-bit.
int16_t Array16[2 *sizeof(void *)/sizeof(int16_t)]
Array of 16-bit signed integrals of length 8 on 64-bit platform, 4 on a 32-bit platform.
uint16_t UInt16
16-bit unsigned integral.
int32_t Array32[2 *sizeof(void *)/sizeof(int32_t)]
Array of 32-bit signed integrals of length 4 on a 64-bit platform. Not available on 32-bit platforms.
int64_t Int64
64-bit signed integral. Available only if platform is not of 64-bit.
integer Int
Signed integral of platform-dependent size.
int64_t Array64[2 *sizeof(void *)/sizeof(int64_t)]
Array of 64-bit signed integrals of length 1 on a 32-bit platform. Not available on 64-bit platforms.
uinteger UInt
Unsigned integral of platform-dependent size.
uint8_t UInt8
8-bit unsigned integral.
int8_t Array8[2 *sizeof(void *)/sizeof(int8_t)]
Array of 8-bit signed integrals of length 16 on 64-bit platform, 8 on a 32-bit platform.
uint32_t UInt32
32-bit unsigned integral. Available only if platform is not of 32-bit.
integer Array[2]
Array of 64-bit signed integrals of length two on 64-bit platform, one on a 32-bit platform.
uinteger UArray[2]
Array of 64-bit unsigned integrals of length two on 64-bit platform, one on a 32-bit platform.
constexpr UnionIntegrals(integer v1, integer v2)
int16_t Int16
16-bit signed integral.