ALib C++ Library
Library Version: 2511 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 two on 64-bit platform, one on a 32-bit platform.
96 uinteger UArray [ 2 ]; ///< Array of 64-bit unsigned integrals of length two on 64-bit platform, one 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 four on 64-bit platform, two on a 32-bit platform.
191 float FloatArray [2 * sizeof(void*) / sizeof(float )];
192
193 /// Array of \c double. The Length is usually two on 64-bit platform, one 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 )
338 : Array( tpointer, integer(length)) {}
339
340 #endif
341
342 /// Returns a pointer of type \c void*.
343 /// @return The pointer stored.
344 constexpr const void* GetVoidPointer() const { return VoidP; }
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* GetPointer () const { 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 { return reinterpret_cast<TReturn*>(PointerPairMutable.P2); }
357
358 /// Sets a pointer of type \c char*.
359 /// @param value The value to set.
360 constexpr void SetPointer ( void* value ) { PointerPair.P1= value; }
361
362 /// Returns the length of a stored array (the second word stored).
363 /// @return The length stored.
364 constexpr integer GetLength () const { return Array.Length; }
365
366 /// Clears this box data.
367 ///
368 /// It has to be ensured that all the memory used by a mapped type is cleared.
369 /// For example, the default implementations of box-functions \alib{boxing;FHashcode} and
370 /// \alib{boxing;FEquals} are using the relevant bytes of this placeholder, and those must
371 /// not be of random value.
372 ///
373 /// For efficiency reasons, the rest should not be cleared.
374 ///
375 /// @tparam UsageLength The number of bytes to clear.
376 template<unsigned UsageLength>
377 constexpr void Clear() {
378 static_assert( UsageLength > 0 && ( UsageLength <= 2 * sizeof(void*) ),
379 "Invalid usage length given" );
380
381 PointerPair.P1= nullptr;
382 if constexpr( UsageLength > sizeof(void*) )
383 PointerPair.P2= nullptr;
384 }
385
386 //################################################################################################
387 //############################################# Boxing ###########################################
388 //################################################################################################
389
390 /// This version of the overloaded method writes integral values.
391 /// @tparam TIntegral The integral type to store.
392 /// @param value The value to store.
393 template<typename TIntegral>
394 requires( std::is_integral_v<TIntegral> )
395 constexpr void Write( const TIntegral& value ) {
396
397 if constexpr( std::is_signed_v<TIntegral>) {
398
399 if constexpr( sizeof(TIntegral) == 1 ) Integrals.Int8 = int8_t (value);
400 else if constexpr( sizeof(TIntegral) == 2 ) Integrals.Int16 = int16_t(value);
401 #if ALIB_SIZEOF_INTEGER == 4
402 else if constexpr( sizeof(TIntegral) == 4 ) Integrals.Int = integer(value);
403 else if constexpr( sizeof(TIntegral) == 8 ) Integrals.Int64 = int64_t(value);
404 #else
405 else if constexpr( sizeof(TIntegral) == 4 ) Integrals.Int32 = int32_t(value);
406 else if constexpr( sizeof(TIntegral) == 8 ) Integrals.Int = integer(value);
407 #endif
408 } else {
409 if constexpr( sizeof(TIntegral) == 1 ) Integrals.UInt8 = uint8_t (value);
410 else if constexpr( sizeof(TIntegral) == 2 ) Integrals.UInt16= uint16_t(value);
411 #if ALIB_SIZEOF_INTEGER == 4
412 else if constexpr( sizeof(TIntegral) == 4 ) Integrals.UInt = uinteger(value);
413 else if constexpr( sizeof(TIntegral) == 8 ) Integrals.UInt64= uint64_t(value);
414 #else
415 else if constexpr( sizeof(TIntegral) == 4 ) Integrals.UInt32= uint32_t(value);
416 else if constexpr( sizeof(TIntegral) == 8 ) Integrals.UInt = uinteger(value);
417 #endif
418 } }
419
420 /// This version of the overloaded method writes floating-point values.
421 /// @tparam TFloat The integral type to store.
422 /// @param value The value to store.
423 template<typename TFloat>
424 requires( std::is_floating_point_v<TFloat> )
425 constexpr void Write( const TFloat& value ) {
426 if constexpr( std::same_as<TFloat, float > ) FloatingPoints.Float = value;
427 else if constexpr( std::same_as<TFloat, double> ) FloatingPoints.Double= value;
428 }
429
430 /// This version of the overloaded method requires type \p{TTrivial} to:
431 /// - be trivially copyable,
432 /// - be a non-pointer type,
433 /// - that it fits into the placeholder which is of <c>2 * sizeof(void*)</c>, and
434 /// - is not integral (handled with other overload).
435 ///
436 /// This version copies the value using \c std::bit_cast.
437 /// This way, this method may be used with \c constexpr use cases that enable
438 /// \ref alib_boxing_more_opt_constexpr "constant boxes", which in seldom cases may be required.
439 /// @tparam TTrivial The mapped type to store.
440 /// @param value The value of type \p{TTrivial} to store.
441 template<typename TTrivial>
442 requires( std::is_trivially_copyable_v<TTrivial>
443 && (sizeof(TTrivial) <= 2 * sizeof(void*))
444 && !std::is_pointer_v<TTrivial>
445 && !std::is_integral_v<TTrivial>
446 && !std::is_floating_point_v<TTrivial> )
447 constexpr void Write( const TTrivial& value ) {
448
449 if constexpr( sizeof(TTrivial) == 1 ) Bytes.C1 = std::bit_cast<std::array<char, 1>>( value );
450 else if constexpr( sizeof(TTrivial) == 2 ) Bytes.C2 = std::bit_cast<std::array<char, 2>>( value );
451 else if constexpr( sizeof(TTrivial) == 3 ) Bytes.C3 = std::bit_cast<std::array<char, 3>>( value );
452 else if constexpr( sizeof(TTrivial) == 4 ) Bytes.C4 = std::bit_cast<std::array<char, 4>>( value );
453 else if constexpr( sizeof(TTrivial) == 5 ) Bytes.C5 = std::bit_cast<std::array<char, 5>>( value );
454 else if constexpr( sizeof(TTrivial) == 6 ) Bytes.C6 = std::bit_cast<std::array<char, 6>>( value );
455 else if constexpr( sizeof(TTrivial) == 7 ) Bytes.C7 = std::bit_cast<std::array<char, 7>>( value );
456 else if constexpr( sizeof(TTrivial) == 8 ) Bytes.C8 = std::bit_cast<std::array<char, 8>>( value );
457 #if ALIB_SIZEOF_INTEGER == 8
458 else if constexpr( sizeof(TTrivial) == 9 ) Bytes.C9 = std::bit_cast<std::array<char, 9>>( value );
459 else if constexpr( sizeof(TTrivial) == 10 ) Bytes.C10= std::bit_cast<std::array<char, 10>>( value );
460 else if constexpr( sizeof(TTrivial) == 11 ) Bytes.C11= std::bit_cast<std::array<char, 11>>( value );
461 else if constexpr( sizeof(TTrivial) == 12 ) Bytes.C12= std::bit_cast<std::array<char, 12>>( value );
462 else if constexpr( sizeof(TTrivial) == 13 ) Bytes.C13= std::bit_cast<std::array<char, 13>>( value );
463 else if constexpr( sizeof(TTrivial) == 14 ) Bytes.C14= std::bit_cast<std::array<char, 14>>( value );
464 else if constexpr( sizeof(TTrivial) == 15 ) Bytes.C15= std::bit_cast<std::array<char, 15>>( value );
465 else if constexpr( sizeof(TTrivial) == 16 ) Bytes.C16= std::bit_cast<std::array<char, 16>>( value );
466 #endif
467 }
468
469 /// Writes two values of arbitrary type into the placeholder.
470 /// The method requires that:
471 /// - Types \p{T1} and \p{T2} are trivially copyable
472 /// - The types are no pointers
473 /// - The sum of the types' sizes fit into the placeholder which is of <c>2 * sizeof(void*)</c>.
474 ///
475 /// This version packs the two given values with no gap into a tuple and then writes that
476 /// tuple using \c std::bit_cast. This way, this method may be used with \c constexpr
477 /// use cases that enable \ref alib_boxing_more_opt_constexpr "constant boxes", which in
478 /// seldom cases may be required. Furthermore, this way, values boxed like this fulfil one
479 /// requirement to produce reliable \ref alib_boxing_customizing_placeholder "hash values".
480 ///
481 /// @tparam T1 The type of the first value to store.
482 /// @tparam T2 The type of the second value to store.
483 /// @param v1 The first value to store.
484 /// @param v2 The second value to store.
485 template<typename T1, typename T2>
486 requires( std::is_trivially_copyable_v<T1> && !std::is_pointer_v<T1>
487 && std::is_trivially_copyable_v<T2> && !std::is_pointer_v<T2>
488 && ( sizeof(T1) + sizeof(T2) <= 2 * sizeof(void*) )
489 )
490 constexpr void Write( const T1& v1, const T2& v2 ) {
491
492 // Force the struct to be packed (i.e. no padding in between)
493 #pragma pack(push, 1)
494 struct PackedTuple {
495 T1 first;
496 T2 second;
497 };
498 #pragma pack(pop)
499
500 static_assert(sizeof(PackedTuple) == sizeof(T1) + sizeof(T2),
501 "PackedTuple has unexpected padding!");
502
503 // Pack the two values into our packed tuple.
504 PackedTuple value { v1, v2 };
505
506 if constexpr( sizeof(PackedTuple) == 1 ) Bytes.C1 = std::bit_cast<std::array<char, 1>>( value );
507 else if constexpr( sizeof(PackedTuple) == 2 ) Bytes.C2 = std::bit_cast<std::array<char, 2>>( value );
508 else if constexpr( sizeof(PackedTuple) == 3 ) Bytes.C3 = std::bit_cast<std::array<char, 3>>( value );
509 else if constexpr( sizeof(PackedTuple) == 4 ) Bytes.C4 = std::bit_cast<std::array<char, 4>>( value );
510 else if constexpr( sizeof(PackedTuple) == 5 ) Bytes.C5 = std::bit_cast<std::array<char, 5>>( value );
511 else if constexpr( sizeof(PackedTuple) == 6 ) Bytes.C6 = std::bit_cast<std::array<char, 6>>( value );
512 else if constexpr( sizeof(PackedTuple) == 7 ) Bytes.C7 = std::bit_cast<std::array<char, 7>>( value );
513 else if constexpr( sizeof(PackedTuple) == 8 ) Bytes.C8 = std::bit_cast<std::array<char, 8>>( value );
514 #if ALIB_SIZEOF_INTEGER == 8
515 else if constexpr( sizeof(PackedTuple) == 9 ) Bytes.C9 = std::bit_cast<std::array<char, 9>>( value );
516 else if constexpr( sizeof(PackedTuple) == 10 ) Bytes.C10= std::bit_cast<std::array<char, 10>>( value );
517 else if constexpr( sizeof(PackedTuple) == 11 ) Bytes.C11= std::bit_cast<std::array<char, 11>>( value );
518 else if constexpr( sizeof(PackedTuple) == 12 ) Bytes.C12= std::bit_cast<std::array<char, 12>>( value );
519 else if constexpr( sizeof(PackedTuple) == 13 ) Bytes.C13= std::bit_cast<std::array<char, 13>>( value );
520 else if constexpr( sizeof(PackedTuple) == 14 ) Bytes.C14= std::bit_cast<std::array<char, 14>>( value );
521 else if constexpr( sizeof(PackedTuple) == 15 ) Bytes.C15= std::bit_cast<std::array<char, 15>>( value );
522 else if constexpr( sizeof(PackedTuple) == 16 ) Bytes.C16= std::bit_cast<std::array<char, 16>>( value );
523 #endif
524 }
525
526 /// This version of the overloaded method is used for boxing C++ pointer types.
527 ///
528 /// Note that for unboxing custom types from C++ array types, a custom implementation of
529 /// \alib{boxing;BoxTraits::Read} is needed. Such an implementation reads the pointer and length
530 /// directly from this struct. In other words, there is no overloaded method \b Read
531 /// available for array types.
532 ///
533 /// @tparam TPointer The pointer type.
534 /// @param pointer The pointer to store.
535 template<typename TPointer>
536 constexpr void Write( const TPointer* pointer) { *this= Placeholder(pointer); }
537
538 /// This version of the overloaded method is used for boxing C++ array types.
539 /// The type and length of the array is stored in the field #Array of the data union.
540 ///
541 /// Note that for unboxing custom types from C++ array types, a custom implementation of
542 /// \alib{boxing;BoxTraits::Read} is needed. Such an implementation reads the pointer and length
543 /// directly from this struct.
544 /// (I.e. there is no overloaded method \b Read available for arrays.)
545 ///
546 /// @tparam TArray The pointer type.
547 /// @param pointer The pointer to store.
548 /// @param length The array's length to store.
549 template<typename TArray>
550 constexpr void Write( const TArray* pointer, integer length )
551 { *this= Placeholder(pointer, length); }
552
553 /// This version of the overloaded method is used for boxing two pointer types.
554 /// @tparam TP1 The type of the first pointer.
555 /// @tparam TP2 The type of the second pointer.
556 /// @param p1 The first pointer to store.
557 /// @param p2 The second pointer to store.
558 template<typename TP1, typename TP2>
559 constexpr void Write( const TP1* p1, const TP2* p2 ) { *this= Placeholder(p1, p2); }
560
561 /// This version of the overloaded method is selected when the type \p{TPointer} is not
562 /// trivially copyable, still fits into the placeholder of size <c>2 * sizeof(void*)</c>, and
563 /// furthermore does not directly match other overloaded versions.
564 ///
565 /// The copying is performed using \c memcpy.
566 /// This is necessary to avoid de-referencing type-punned pointers which would
567 /// break the strict-aliasing rule when compiling the code with higher optimization levels.
568 /// Note that modern compilers like GCC usually optimize the invocation of \c memcpy out.<br>
569 ///
570 /// This version is \b not \c constexpr and thus may not be used to create
571 /// \ref alib_boxing_more_opt_constexpr "constant boxes", which in
572 /// seldom cases may be required.
573 ///
574 /// @tparam TPointer The type of the value to store.
575 /// @param value The value to store.
576 template<typename TPointer>
577 requires( !std::is_trivially_copyable_v<TPointer>
578 && (sizeof(TPointer) <= 2 * sizeof(void*)) )
579 void Write( const TPointer& value )
580 { std::memcpy( &PointerPair.P1, &value, sizeof(TPointer) ); }
581
582 //################################################################################################
583 //############################################ Unboxing ##########################################
584 //################################################################################################
585 /// Templated read method for value types that fit into this placeholder, which are not
586 /// pointers, and furthermore which are not trivially copyable.
587 ///
588 /// The value is dereferenced from the start of the placeholder memory using
589 /// \c reinterpret_cast. With that, this overload is never \c constexpr and thus not marked
590 /// so.
591 ///
592 /// @tparam TMapped The value type to unbox.
593 /// @return The value stored.
594 template<typename TMapped>
595 requires( !( std::is_trivially_copyable_v<TMapped>
596 && (sizeof(TMapped) <= 2 * sizeof(void*)) )
597 && !std::is_pointer_v<TMapped>
598 )
599 TMapped Read() const { return *reinterpret_cast<const TMapped*>( this ); }
600
601 /// Templated method to read pointer types from this placeholder.
602 /// The pointer is changed to the desired type using \c reinterpret_cast.
603 /// Only if <c>void*</c> is requested, this method can be \c constexpr.
604 /// @tparam TPointer The pointer-type to unbox.
605 /// @return The value stored.
606 template<typename TPointer>
607 requires std::is_pointer_v<TPointer>
608 constexpr TPointer Read() const {
609 if constexpr (std::same_as<TPointer, void*>)
610 return VoidP;
611 return reinterpret_cast<TPointer>( VoidP );
612 }
613
614 /// Templated method to read integral types from this placeholder.
615 /// @tparam TIntegral The integral type to unbox.
616 /// @return The value stored.
617 template<typename TIntegral>
618 requires( std::is_integral_v<TIntegral> )
619 constexpr TIntegral Read() const {
620
621 if constexpr( std::is_signed_v<TIntegral>) {
622
623 if constexpr( sizeof(TIntegral) == 1 ) return TIntegral(Integrals.Int8 );
624 else if constexpr( sizeof(TIntegral) == 2 ) return TIntegral(Integrals.Int16);
625 #if ALIB_SIZEOF_INTEGER == 4
626 else if constexpr( sizeof(TIntegral) == 4 ) return TIntegral(Integrals.Int );
627 else if constexpr( sizeof(TIntegral) == 8 ) return TIntegral(Integrals.Int64);
628 #else
629 else if constexpr( sizeof(TIntegral) == 4 ) return TIntegral(Integrals.Int32);
630 else if constexpr( sizeof(TIntegral) == 8 ) return TIntegral(Integrals.Int );
631 #endif
632 } else {
633 if constexpr( sizeof(TIntegral) == 1 ) return TIntegral(Integrals.UInt8 );
634 else if constexpr( sizeof(TIntegral) == 2 ) return TIntegral(Integrals.UInt16);
635 #if ALIB_SIZEOF_INTEGER == 4
636 else if constexpr( sizeof(TIntegral) == 4 ) return TIntegral(Integrals.UInt );
637 else if constexpr( sizeof(TIntegral) == 8 ) return TIntegral(Integrals.UInt64);
638 #else
639 else if constexpr( sizeof(TIntegral) == 4 ) return TIntegral(Integrals.UInt32);
640 else if constexpr( sizeof(TIntegral) == 8 ) return TIntegral(Integrals.UInt );
641 #endif
642 } }
643
644 /// Templated method to read integral types from this placeholder.
645 /// @tparam TFloat The integral type to unbox.
646 /// @return The value stored.
647 template<typename TFloat>
648 requires( std::is_floating_point_v<TFloat> )
649 constexpr TFloat Read() const {
650 if constexpr( std::same_as<TFloat, float > ) return TFloat(FloatingPoints.Float );
651 else if constexpr( std::same_as<TFloat, double> ) return TFloat(FloatingPoints.Double);
652 }
653
654 /// Templated method to read trivially copyable types that fits into the placeholder.
655 /// @tparam TTrivial The type to read from this placeholder.
656 /// @return The stored value.
657 template<typename TTrivial>
658 requires( std::is_trivially_copyable_v<TTrivial>
659 && (sizeof(TTrivial) <= 2 * sizeof(void*))
660 && !std::is_pointer_v<TTrivial>
661 && !std::is_integral_v<TTrivial>
662 && !std::is_floating_point_v<TTrivial>
663 )
664 constexpr TTrivial Read() const {
665
666 if constexpr( sizeof(TTrivial) == 1 ) return std::bit_cast<TTrivial>(Bytes.C1);
667 else if constexpr( sizeof(TTrivial) == 2 ) return std::bit_cast<TTrivial>(Bytes.C2);
668 else if constexpr( sizeof(TTrivial) == 3 ) return std::bit_cast<TTrivial>(Bytes.C3);
669 else if constexpr( sizeof(TTrivial) == 4 ) return std::bit_cast<TTrivial>(Bytes.C4);
670 else if constexpr( sizeof(TTrivial) == 5 ) return std::bit_cast<TTrivial>(Bytes.C5);
671 else if constexpr( sizeof(TTrivial) == 6 ) return std::bit_cast<TTrivial>(Bytes.C6);
672 else if constexpr( sizeof(TTrivial) == 7 ) return std::bit_cast<TTrivial>(Bytes.C7);
673 else if constexpr( sizeof(TTrivial) == 8 ) return std::bit_cast<TTrivial>(Bytes.C8);
674 #if ALIB_SIZEOF_INTEGER == 8
675 else if constexpr( sizeof(TTrivial) == 9 ) return std::bit_cast<TTrivial>(Bytes.C9 );
676 else if constexpr( sizeof(TTrivial) == 10 ) return std::bit_cast<TTrivial>(Bytes.C10);
677 else if constexpr( sizeof(TTrivial) == 11 ) return std::bit_cast<TTrivial>(Bytes.C11);
678 else if constexpr( sizeof(TTrivial) == 12 ) return std::bit_cast<TTrivial>(Bytes.C12);
679 else if constexpr( sizeof(TTrivial) == 13 ) return std::bit_cast<TTrivial>(Bytes.C13);
680 else if constexpr( sizeof(TTrivial) == 14 ) return std::bit_cast<TTrivial>(Bytes.C14);
681 else if constexpr( sizeof(TTrivial) == 15 ) return std::bit_cast<TTrivial>(Bytes.C15);
682 else if constexpr( sizeof(TTrivial) == 16 ) return std::bit_cast<TTrivial>(Bytes.C16);
683 #endif
684 }
685
686
687 /// Templated method that reads two trivially copyable types that jointly fit into the
688 /// placeholder.
689 /// @tparam TTrivial1 The type of the first value to read from this placeholder.
690 /// @tparam TTrivial2 The type of the second value to read from this placeholder.
691 /// @param v1 Output parameter. Receives the first value.
692 /// @param v2 Output parameter. Receives the second value.
693 template<typename TTrivial1, typename TTrivial2>
694 requires( std::is_trivially_copyable_v<TTrivial1> && !std::is_pointer_v<TTrivial1>
695 && std::is_trivially_copyable_v<TTrivial2> && !std::is_pointer_v<TTrivial2>
696 && ( sizeof(TTrivial1) + sizeof(TTrivial2) <= 2 * sizeof(void*) )
697 )
698 constexpr void Read(TTrivial1& v1, TTrivial2& v2) const {
699
700 // Ensure the packed structure has no gaps.
701 #pragma pack(push, 1)
702 struct PT {
703 TTrivial1 v1;
704 TTrivial2 v2;
705 };
706 #pragma pack(pop)
707
708 static_assert(sizeof(PT) == sizeof(TTrivial1) + sizeof(TTrivial2),
709 "PackedTuple has unexpected padding!");
710
711 // Depending on the size of PackedTuple, bit_cast from the corresponding member
712 // of Bytes to our packed structure, then assign the values.
713 if constexpr( sizeof(PT) == 1 ) { const auto pt = std::bit_cast<PT>(Bytes.C1); v1 = pt.v1; v2 = pt.v2; }
714 else if constexpr( sizeof(PT) == 2 ) { const auto pt = std::bit_cast<PT>(Bytes.C2); v1 = pt.v1; v2 = pt.v2; }
715 else if constexpr( sizeof(PT) == 3 ) { const auto pt = std::bit_cast<PT>(Bytes.C3); v1 = pt.v1; v2 = pt.v2; }
716 else if constexpr( sizeof(PT) == 4 ) { const auto pt = std::bit_cast<PT>(Bytes.C4); v1 = pt.v1; v2 = pt.v2; }
717 else if constexpr( sizeof(PT) == 5 ) { const auto pt = std::bit_cast<PT>(Bytes.C5); v1 = pt.v1; v2 = pt.v2; }
718 else if constexpr( sizeof(PT) == 6 ) { const auto pt = std::bit_cast<PT>(Bytes.C6); v1 = pt.v1; v2 = pt.v2; }
719 else if constexpr( sizeof(PT) == 7 ) { const auto pt = std::bit_cast<PT>(Bytes.C7); v1 = pt.v1; v2 = pt.v2; }
720 else if constexpr( sizeof(PT) == 8 ) { const auto pt = std::bit_cast<PT>(Bytes.C8); v1 = pt.v1; v2 = pt.v2; }
721 #if ALIB_SIZEOF_INTEGER > 4
722 else if constexpr( sizeof(PT) == 9 ) { const auto pt = std::bit_cast<PT>(Bytes.C9); v1 = pt.v1; v2 = pt.v2; }
723 else if constexpr( sizeof(PT) == 10 ) { const auto pt = std::bit_cast<PT>(Bytes.C10); v1 = pt.v1; v2 = pt.v2; }
724 else if constexpr( sizeof(PT) == 11 ) { const auto pt = std::bit_cast<PT>(Bytes.C11); v1 = pt.v1; v2 = pt.v2; }
725 else if constexpr( sizeof(PT) == 12 ) { const auto pt = std::bit_cast<PT>(Bytes.C12); v1 = pt.v1; v2 = pt.v2; }
726 else if constexpr( sizeof(PT) == 13 ) { const auto pt = std::bit_cast<PT>(Bytes.C13); v1 = pt.v1; v2 = pt.v2; }
727 else if constexpr( sizeof(PT) == 14 ) { const auto pt = std::bit_cast<PT>(Bytes.C14); v1 = pt.v1; v2 = pt.v2; }
728 else if constexpr( sizeof(PT) == 15 ) { const auto pt = std::bit_cast<PT>(Bytes.C15); v1 = pt.v1; v2 = pt.v2; }
729 else if constexpr( sizeof(PT) == 16 ) { const auto pt = std::bit_cast<PT>(Bytes.C16); v1 = pt.v1; v2 = pt.v2; }
730 #endif
731 }
732}; // union Placeholder
733
734static_assert( sizeof(Placeholder) == 2 * sizeof(std::size_t),
735 "Size of boxing::Placeholder is not two times the size of 'size_t'. "
736 "Compilation platform not supported." );
737
738
739//==================================================================================================
740/// This is a simple helper type that forms a pair of two values. It is useful when the boxing of
741/// such a pair is wanted. The benefit over using <c>std::pair</c> is, that this struct does not
742/// provide any extras but the plain public values, and thus it is trivially copyable in the case
743/// that both types are.
744/// @tparam T1 The type of the first value.
745/// @tparam T2 The type of the second first value.
746//==================================================================================================
747template <typename T1, typename T2>
748requires ( std::is_trivially_copyable_v<T1>
749 && std::is_trivially_copyable_v<T2>
750 && ( sizeof(T1) + sizeof(T2) <= 2 * sizeof(void*) )
751 )
752struct Pair {
753 T1 First; ///< The first value.
754 T2 Second; ///< The second value.
755};
756
757//==================================================================================================
758/// This is a simple helper function that constructs a \alib{boxing;Pair}.
759/// @tparam T1 The type of the first value. Deduced by the compiler.
760/// @tparam T2 The type of the second first value. Deduced by the compiler.
761/// @param t1 The first value.
762/// @param t2 The second value.
763/// @return The pair of deduced types.
764//==================================================================================================
765template <typename T1, typename T2>
766requires ( std::is_trivially_copyable_v<T1>
767 && std::is_trivially_copyable_v<T2>
768 && ( sizeof(T1) + sizeof(T2) <= 2 * sizeof(void*) )
769 )
770constexpr Pair<T1, T2> MakePair( const T1& t1, const T2& t2) { return Pair<T1, T2>{t1, t2}; }
771
772}
773
774/// Type alias in namespace \b alib.
775template <typename T, typename U>
777
778} // namespace [alib::boxing]
#define ALIB_EXPORT
Definition alib.inl:497
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 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.