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