ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
boxingtraits.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 {
9
10/// This is an empty type used to denote that default boxing is active. The type is
11/// used with the default implementation of the type trait #"BoxTraits".
12/// Providing this type in custom specializations, makes such specialization in-effective.
13/// \see Templated struct #"BoxTraits" for more information.
15
16/// This is an empty type used to denote that a type must not be boxed.
17/// To disable \alib_boxing_nl for a custom type, a specialization of type-traits
18/// struct #"BoxTraits" must use this type to define type alias
19/// #"BoxTraits::Mapping".
20///
21/// \see
22/// Templated struct #"BoxTraits" for more information.
23struct NotBoxableTag {};
24
25
26//==================================================================================================
27/// This template struct is used to define customized behavior for boxing C++ type
28/// \p{TBoxable}.
29///
30/// ### Default Boxing: ###
31/// If this struct is \b not specialized for template type \p{TBoxable},
32/// default boxing applies.
33/// With that, values \b and pointers of a type are boxed in the same way:
34/// - They are both boxed to a pointer of \p{TBoxable} if a value of the type does not "fit" into
35/// a box's #"boxing::Placeholder" or if the type is not copy-constructible or not
36/// trivially destructible.
37/// - Otherwise, both are boxed as values, hence if a pointer is given to the constructor or
38/// assign \c operator= of class #"Box", indirection \c operator* is applied.
39///
40/// <br><p>
41/// ### Custom Boxing With Specializations Of This Struct: ###
42/// The default boxing briefly described above, can be manipulated by providing a specialization
43/// of this struct.
44/// All three entities of this default implementation have to be provided with such specializations:
45///
46/// 1. <b>Type alias #".Mapping":</b><br>
47/// This alias defines the type that a value of \p{TBoxable} is converted to when boxed.
48///
49/// If special type #"NotBoxableTag" is given, then boxing is disallowed for
50/// type \p{TBoxed}.
51/// In this case, only declarations of functions \b Write"and \b Read need to be provided, as
52/// they are never invoked.
53///
54/// The default implementation of this struct uses a second special type available,
55/// #"DefaultBoxingTag" which denotes default boxing.
56///
57/// 2. <b>Boolean constexpr value #".IsArray":</b><br>
58/// If set to \c true, array-boxing is performed. In this case #".Mapping" denotes the element
59/// type of the boxed C++ array.
60///
61/// 3. <b>Static Method #".Write":</b><br>
62/// This method is invoked for converting source values to mapped types.
63/// Often the default implementation given in the default version of this struct is
64/// applicable. In this case it can simply be copied to the specialization.
65///
66/// Custom implementations may use one of the overloads of method
67/// #"Placeholder::Write(const TInt)" or alternatively write to the \c union members of the
68/// placeholder directly.
69///
70/// 4. <b>Static Method #".Read":</b><br>
71/// This method is invoked for converting the placeholder data back to the boxed source type.
72/// Often the default implementation given in the default version of this struct is
73/// well suited. In this case it can simply be copied to the specialization.<br>
74/// Custom implementations may use one of the overloads of method #"Placeholder::Read"
75/// or alternatively read the \c union members of the placeholder directly.
76///
77/// A type becomes #"IsUnboxable;not unboxable" at the moment this function
78/// returns a different type than \p{TBoxable}.<br>
79/// This may well be intended, and thus the specialized version of this struct
80/// may declare this method to return \c void.
81/// In this case a declaration of the method is enough, as it will never be invoked.<br>
82/// With conditional specializations of this struct (see Programmer's Manual
83/// chapter #"alib_boxing_customizing_conditional"), the return type might be likewise
84/// conditionally set.
85/// Such a return type then decides which of the types are unboxable and which are not.
86///
87/// \note
88/// If a specialization uses default type mapping by providing<br>
89///
90/// using Mapping= DefaultBoxingTag;
91/// \note
92/// in theory, a mixture of default and custom boxing is in place!
93/// Note, that the authors of this library and documentation have not seen a use case for this,
94/// yet.
95///
96/// <br><p>
97/// ### Helper Macros: ###
98///
99/// A set of macros for defining specializations exist.
100/// The use of the macro is recommended, as besides being less error-prone, their use makes the code
101/// more readable. Finally, chances are good that code that uses the macros remains compatible
102/// with future versions of module \alib_boxing_nl.
103///
104/// All macros expect this struct's template type \p{TBoxable} as the first parameter, and most
105/// expect the mapped type as the second parameter.
106///
107/// For more information, see the reference documentation of the macros, which are:
108///
109/// - #"ALIB_BOXING_CUSTOMIZE"
110/// - #"ALIB_BOXING_CUSTOMIZE_TYPE_MAPPING"
111/// - #"ALIB_BOXING_CUSTOMIZE_NOT_UNBOXABLE"
112/// - #"ALIB_BOXING_CUSTOMIZE_ARRAY_TYPE"
113/// - #"ALIB_BOXING_CUSTOMIZE_ARRAY_TYPE_NON_UNBOXABLE"
114/// - #"ALIB_BOXING_CUSTOMIZE_DENY_BOXING"
115///
116/// <br><p>
117/// ### Value Boxing and Nulled Pointers: ###
118/// If a type is boxed as value (either with default boxing or custom boxing) and a \e nulled
119/// pointer to that type is boxed, method #"Placeholder::Clear" is invoked
120/// instead of this struct's method #".Write".
121///
122/// <br><p>
123/// ### Avoiding Seldom Compilation Errors: ###
124/// For technical reasons, namely to avoid compilation problems, some conditional specialization
125/// is internally made that declare method #".Read" \c void. This is sometimes needed if a type is
126/// not returnable, even if that becomes never be boxed or unboxed.
127/// Such a situation might happen, for example, if class #"%Box" is part of a \c union.<br>
128/// The specialization is made for the following types of \p{TBoxable}:
129/// - C++ array types<br>
130/// Those types are fetched by an overloaded constructor that does not (because it cannot)
131/// leverage this struct. Likewise, they cannot be unboxed in the usual fashion.
132/// - Function types (selected by <c>std::is_function<TBoxable>::value</c>).
133///
134/// If a dubious error message about method #".Read" not being able to return a certain type occurs,
135/// even one that a user's code even "actively" tries to box or unbox, then it might be helpful
136/// to specialize this struct for such a type, without providing implementations of #"%.Write" and
137/// #"%.Read" and with the latter to return \c void.
138///
139/// <br><p>
140/// \see More explanation and sample code is given in chapter
141/// #"alib_boxing_customizing" of the
142/// #"alib_mod_boxing;Programmer's Manual" of module \alib_boxing_nl.
143///
144/// @tparam TBoxable The source type to customize boxing for.
145//==================================================================================================
146template<typename TBoxable>
147struct BoxTraits {
148 /// Defines the mapped type.
149 /// Special designator types #"DefaultBoxingTag" and
150 /// #"NotBoxableTag" may be given to denote corresponding behavior.
151 /// The default implementation specifies designator type #"%DefaultBoxingTag", which
152 /// disables custom boxing.
154
155 /// Denotes whether type \p{TBoxable} is boxed as an array-type or not.
156 static constexpr bool IsArray= false;
157
158 /// Used for boxing a value, precisely writing the boxable portion of a type into
159 /// field #"Box::data", which is given with parameter \p{box}.<br>
160 /// The default implementation of this struct implements this method as follows:
161 /// \snippet "boxing/boxingtraits.hpp" DOX_BOXING_T_BOXER_WRITE
162 ///
163 /// This implementation leverages the overloaded method #"Placeholder::Write(const TInt)"
164 /// and is often all that is needed with custom specializations.
165 /// @param box The placeholder of the destination box.
166 /// @param value The value to that is to be boxed.
167 DOX_MARKER([DOX_BOXING_T_BOXER_WRITE])
168 static constexpr void Write( Placeholder& box, const TBoxable& value ) { box.Write( value ); }
169 DOX_MARKER([DOX_BOXING_T_BOXER_WRITE])
170
171 /// Used for unboxing a value, precisely reading the contents of field #"Box::data",
172 /// which is given with output parameter \p{box} and creating a value of type \p{TBoxable} from
173 /// that.<br>
174 /// The default implementation of this struct implements this method as follows:
175 /// \snippet "boxing/boxingtraits.hpp" DOX_BOXING_T_BOXER_READ
176 ///
177 /// This implementation leverages the overloaded method #"Placeholder::Read"
178 /// and is often all that is needed with custom specializations.
179 ///
180 /// If a different type than \p{TBoxable} is returned, then that source type is not unboxable.
181 /// To intend such behavior, for example, because \p{TBoxable} is mapped to a reduced type
182 /// and therefore unboxing is not possible, specializations may declare the return type to be
183 /// \c void and omit a definition of this method.<br>
184 /// With conditional customizations, also other return types may be given, which likewise denote
185 /// an unboxable source type. A sample of that is given with Programmer's Manual chapter
186 /// #"alib_boxing_customizing_conditional".
187 ///
188 /// @param box The Placeholder to unbox the data from
189 /// @return \c The unboxed object.
190 static
191 std::conditional_t<!std::is_abstract<TBoxable>::value, TBoxable, TBoxable&>
192 Read( const Placeholder& box) {
193DOX_MARKER([DOX_BOXING_T_BOXER_READ])
194return box.Read<TBoxable>();
195DOX_MARKER([DOX_BOXING_T_BOXER_READ])
196 }
197
198}; // BoxTraits
199
200//############################## critical specializations of BoxTraits #############################
201#if !DOXYGEN
202
203// This is necessary for types that can't be used as return type in any way, for example
204// types with extents, functions, etc.
205template<typename TBoxable>
206requires( std::is_array_v<TBoxable> || std::is_function<TBoxable>::value )
207struct BoxTraits<TBoxable>
208{
209 using Mapping= DefaultBoxingTag;
210 static constexpr bool IsArray= std::is_array_v<TBoxable>;
211
212 static void Write( Placeholder& box, TBoxable& value );
213 static void Read ( const Placeholder& box);
214};
215
216// void (needed to pass TMP stuff)
217template<>
218struct BoxTraits<void>
219{
220 using Mapping= DefaultBoxingTag;
221 static constexpr bool IsArray= false;
222 static void Write( Placeholder& box, const void* value );
223 static void* Read ( const Placeholder& box);
224};
225
226#endif
227
228//==================================================================================================
229/// This specializable \c constexpr must be set for custom types, if the following applies:
230/// - A custom boxing implements #"BoxTraits::Write" in a way that a different number
231/// of bytes are used in union #"boxing::Placeholder" than the \c sizeof() operator reports
232/// on the mapped type's size.
233/// - if the standard copy constructor (used with default boxing) writes a different size.
234/// - One of the above and no specialized version of both box-functions #"FHashcode"
235/// or #"FEquals" are set.
236///
237/// <b>Background:</b><br>
238/// The default implementations of #"FHashcode" and #"FEquals"
239/// must use the first N "relevant" bytes of the placeholder only. The non-relevant bytes are
240/// not written and therefore must not be taken into account.<br>
241/// To receive the number of relevant bytes, they invoke
242/// #"Box::GetPlaceholderUsageLength" (at runtime).
243/// This value is set at compile-time with the creation of a mapped type's \e vtable.
244/// While for array types, the value is set to the overall size of union #"boxing::Placeholder",
245/// for non-array types, the value of this type trait is used.
246///
247/// It might be surprising, but a built-in specialization exists for even a C++ fundamental type
248/// <c>long double</c>, which is dependent on the compiler/platform.
249/// For example, on GNU/Linux 64-bit, GCC reports \c 16 with <c>sizeof(long double)</c>.
250/// However, if a <c>long double</c> value is copied, e.g with:
251///
252/// *pointerToLongDouble= 3.14L;
253///
254/// then only 10 bytes are written. The reason for this is that <c>sizeof</c> reports the size
255/// needed for alignment when placed in an array of that type.
256/// In the case of \alib_boxing_nl, 6 bytes of the placeholder remain random and therefore
257/// must not be used for hashing or testing values on equality.
258///
259/// @tparam TMappedPlain The mapped type to modify relevant placeholder length for #"%FHashcode"
260/// and #"%FEquals" implementation.
261//==================================================================================================
262template<typename TMappedPlain>
263inline constexpr unsigned SizeTraits= sizeof(TMappedPlain);
264
265/// Specialization of traits-expression for type <c>long double</c>.
266/// The implementation of this type is platform-dependent and may be adopted to a certain
267/// compiler/processor combination by passing the configuration macro
268/// #"ALIB_SIZEOF_LONGDOUBLE_WRITTEN".
269template<>
271
273//==================================================================================================
274/// A concept that is satisfied for types which boxing is customized for, hence for types for which
275/// a specialization of the type trait #"BoxTraits" exists.
276///
277/// \see
278/// Concepts #"IsUnboxable", #"IsLocked" and
279/// #"IsNotBoxable".
280///
281/// @tparam T The type to check.
282//==================================================================================================
283template<typename T>
285= ( std::is_pointer_v<T> || !std::same_as<DefaultBoxingTag, typename BoxTraits< T >::Mapping> )
286 && ( !std::is_pointer_v<T> || !std::same_as<DefaultBoxingTag, typename BoxTraits<ALIB_TVALUE(T)*>::Mapping>);
287
288// Note: besides checking BoxTraits<T>, in the case a pointer is given, we also have to check
289// BoxTraits for the type that results when
290// - the pointer is removed and then
291// - a const is removed and finally
292// - the pointer is added again. Otherwise "const T*" is not deferred to "T*"
293
294/// This concept is for internal use. It is satisfied if the given type \p{T} fits into the
295/// placeholder, is copy-constructible and trivially destructible.
296/// @tparam T The type to test.
297/// @tparam TVal Decayed version of \p{T}. Deduced by the compiler, must not be given.
298template<typename T, typename TVal= ALIB_TVALUE(T)>
299concept IsStdPH= sizeof(std::conditional_t<std::same_as<void, TVal>, void*, TVal>)
300 <= sizeof(Placeholder)
301 && std::is_copy_constructible <TVal>::value
302 && std::is_trivially_destructible<TVal>::value;
303
304/// This concept is for internal use. It is satisfied if the given type \p{T}
305/// has customized boxing with a \c constexpr version of the method <b>BoxTraits<T>::Write</b>
306/// which takes only one parameter, namely an instance of \p{T}.
307/// @tparam T The type to test.
308template<typename T>
310 requires(T& t) { { BoxTraits<T>::Write(t) } -> std::same_as<Placeholder>; };
311
312
313/// This type trait by default inherits \c std::false_type. If specialized for
314/// template type \p{TCharArray} to inherit \c std::true_type, then boxing that type will not be
315/// customized automatically with a corresponding specialization of #"ArrayTraits".
316/// This keeps the customization of boxing open to be performed in a different way.
317///
318/// \see
319/// See manual chapter #"alib_boxing_strings" of the
320/// Programmer's Manual of module \alib_boxing_nl.
321///
322/// @tparam TCharArray The type that #"ArrayTraits" is specialized for but still no
323/// character array boxing should be performed.
324template<typename TCharArray>
325struct SuppressCharArrayBoxingTraits : std::false_type {};
326
327
328template<typename T>
331 && ( characters::ArrayTraits <std::remove_cv_t<T>, nchar>::Access == characters::Policy::Implicit
332 || characters::ArrayTraits <std::remove_cv_t<T>, wchar>::Access == characters::Policy::Implicit
333 || characters::ArrayTraits <std::remove_cv_t<T>, xchar>::Access == characters::Policy::Implicit
334 );
335
336template<typename T>
341 ;
342
343
344//==================================================================================================
345/// A concept that is satisfied for types for which boxing is customized to disable unboxing.
346/// In other words, types for which a specialization of the type trait #"%BoxTraits"
347/// exists which declares method #"BoxTraits::Read" to have a different return type
348/// than \p{T} or \p{T&}.
349///
350/// \see
351/// #"IsUnboxable" for an alternative concept that satisfied if a type can be
352/// unboxed and methods #"Box::IsType" and #"Box::Unbox" do not fail to
353/// compile with that type.
354///
355/// @tparam T The type to check.
356//==================================================================================================
357template<typename T>
358concept IsLocked =
359 !std::same_as<T, std::remove_reference_t<decltype(BoxTraits<T>::Read(std::declval<Placeholder>()))> >;
360
361//==================================================================================================
362/// A concept that is satisfied if:
363///
364/// - boxing was customized for the given type and #"NotBoxableTag" was given as
365/// mapped type, or
366/// - given type is a value type, no customization is given for it, while the corresponding
367/// pointer type has customized boxing with mapped type being #"NotBoxableTag", or
368/// - given type is a pointer type, no customization is given for it, while the corresponding
369/// value type has customized boxing with mapped type being #"NotBoxableTag".
370///
371/// If a type is not boxable, it can be neither boxed nor unboxed.
372///
373/// \see
374/// Concepts #"IsCustomized", #"IsLocked" and
375/// #"IsUnboxable".
376///
377/// @tparam T The type to check.
378//==================================================================================================
379template<typename T>
381 std::same_as<NotBoxableTag, typename BoxTraits<T>::Mapping>
382 || ( !IsCustomized<T>
383 && ( ( !std::is_pointer_v<T>
384 && std::same_as<NotBoxableTag,
385 typename BoxTraits< T*>::Mapping> )
386 || ( std::is_pointer_v<T>
387 && std::same_as<NotBoxableTag,
388 typename BoxTraits<std::remove_pointer_t<T>>::Mapping> ) ) )
389 ;
390
391
392
393
394//==================================================================================================
395/// This concept is satisfied if a type can be unboxed and methods #"Box::IsType"
396/// and #"Box::Unbox" will not fail to compile with that type.
397///
398/// With default boxing, one of the types \p{T} and \p{T*} are unboxable (depending on value type
399/// size and whether the type is copy-constructible and trivially destructible).
400///
401/// If custom boxing for either or both of types \p{T} and \p{T*} is in place, then the given type
402/// is not unboxable if:
403/// - customization is not in place for the version passed (value or pointer).
404/// - customization is in place for the given type but concept #"IsLocked" is satisfied.
405/// - The type is mapped to #"NotBoxableTag".
406///
407/// \see
408/// Concepts #"IsCustomized", #"IsLocked" and
409/// #"IsNotBoxable".
410///
411/// @tparam T The type to check.
412//==================================================================================================
413template<typename T>
415 // default boxing
418 && bool( std::is_pointer_v<T> )
419 == bool( ( sizeof(Placeholder) < sizeof(ALIB_TVALUE(T)) )
420 || !std::is_copy_constructible <ALIB_TVALUE(T)>::value
421 || !std::is_trivially_destructible<ALIB_TVALUE(T)>::value ) )
422 || // custom boxing
424 && !IsLocked <T>
425 && !IsNotBoxable<T> )
426
427 || // string type
429;
430
432
433
434}} // namespace [alib::boxing]
#define ALIB_TVALUE(T)
#define ALIB_ALLOW_DOCS
#define ALIB_POP_ALLOWANCE
#define ALIB_EXPORT
#define ALIB_SIZEOF_LONGDOUBLE_WRITTEN
DOXYGEN.
Definition box.cpp:17
constexpr unsigned SizeTraits
Definition alox.cpp:14
characters::wchar wchar
Type alias in namespace #"%alib".
characters::nchar nchar
Type alias in namespace #"%alib".
characters::xchar xchar
Type alias in namespace #"%alib".
static std::conditional_t<!std::is_abstract< TBoxable >::value, TBoxable, TBoxable & > Read(const Placeholder &box)
static constexpr void Write(Placeholder &box, const TBoxable &value)
DefaultBoxingTag Mapping
static constexpr bool IsArray
Denotes whether type TBoxable is boxed as an array-type or not.
static constexpr Policy Construction