ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
serialization.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header file is part of module \alib_enums of the \aliblong.
4///
5/// \emoji :copyright: 2013-2024 A-Worx GmbH, Germany.
6/// Published under \ref mainpage_license "Boost Software License".
7//==================================================================================================
8#ifndef HPP_ALIB_ENUMS_SERIALIZATION
9#define HPP_ALIB_ENUMS_SERIALIZATION 1
10#pragma once
12
13ALIB_ASSERT_MODULE(STRINGS)
14
15#if ALIB_CAMP
17#endif
20
22
23namespace alib { namespace enums {
24
25// #################################################################################################
26// Parsing (Consume)
27// #################################################################################################
28
29
30#if DOXYGEN
31//==================================================================================================
32/// Consumes an element value of a C++ enumeration that is equipped with
33/// \ref alib_enums_records "ALib Enum Records" of type \alib{enums;ERSerializable} (or of a derived
34/// type) from a given \alib{strings;TSubstring;Substring}.
35///
36/// In debug-builds, the method asserts that at least one record is defined for \p{TEnum}.
37///
38/// For more information consult chapter
39/// \ref alib_enums_records_details_serialization "4.3.1 Serialization/Deserialization" of the
40/// Programmer's Manual of module \alib_enums.
41///
42/// \note
43/// This namespace function is applicable to \alib{enums;T_EnumIsBitwise;bitwise enums} as well.
44/// However, only one element name is parsed.
45/// To parse multiple elements (ored to one resulting enum value), use sibling function
46/// \alib{enums;ParseBitwise}.
47///
48/// @tparam TEnum The enumeration type equipped with <b>ALib Enum Recorss</b>
49/// of type \alib{enums;ERSerializable}.
50/// @tparam TChar The character type of the \p{input} substring.
51/// Deduced by the compiler.
52/// @tparam TSensitivity The sensitivity of the comparison.
53/// Defaults to \b Case::Sensitive.
54/// @tparam TTrimBeforeConsume Determines if the sub string should be (left-) trimmed before the
55/// consume operation. If so, in case of parsing failure, trimming is
56/// \b not restored.<br>
57/// Defaults to \b Whitespaces::Trim.
58/// @param[in,out] input The substring to parse. Passed as reference. On success, the
59/// substring will be shortened by the characters consumed. On failure
60/// only trimmed characters are consumed.
61/// @param[out] result The result enum element given as reference.
62/// @return \c true if an enum element was successfuly recognized, \c false otherwise.
63///
64/// ### Module Dependencies ###
65/// This method is only available if \alib_strings is included in the \alibdist.
66//==================================================================================================
67template<typename TEnum,
68 typename TChar,
69 Case TSensitivity = Case::Ignore,
70 Whitespaces TTrimBeforeConsume = Whitespaces::Trim>
71inline
72bool Parse( strings::TSubstring<TChar>& input, TEnum& result );
73#else
74#if defined(_MSC_VER)
75#pragma warning( push )
76#pragma warning( disable : 4127 )
77#endif
78
79template<typename TEnum,
80 typename TChar,
81 lang::Case TSensitivity = lang::Case::Ignore,
82 lang::Whitespaces TTrimBeforeConsume = lang::Whitespaces::Trim >
83ATMP_T_IF(bool, EnumRecords<TEnum>::template AreOfType<ERSerializable>() )
84Parse( strings::TSubstring<TChar>& input, TEnum& result )
85{
86 ALIB_ASSERT_ERROR( EnumRecords<TEnum>().begin() != EnumRecords<TEnum>().end(), "ENUMS",
87 NString128() << "No Enum Records for type <"
88 << lang::DbgTypeDemangler( typeid(TEnum)).Get() << "> found." )
89 if constexpr ( TTrimBeforeConsume == lang::Whitespaces::Trim )
90 input.TrimStart();
91
92 for( auto recordIt= EnumRecords<TEnum>().begin() ;
93 recordIt != EnumRecords<TEnum>().end() ; ++recordIt )
94 if ( input.template ConsumePartOf<TSensitivity>( recordIt->EnumElementName,
95 recordIt->MinimumRecognitionLength ) > 0 )
96 {
97 result= recordIt.Enum();
98 return true;
99 }
100 return false;
101}
102#endif
103
104#if DOXYGEN
105//==================================================================================================
106/// Repeatedly invokes sibling function \alib{enums;Parse} until \p{delim} is not found.
107/// The enum element values are or'ed in \p{result}.
108///
109/// In debug-builds, the method asserts that at least one record is defined for \p{TEnum}.
110///
111/// \note
112/// This method is applicable only to \alib{enums;T_EnumIsBitwise;bitwise enums} that likewise
113/// are equipped with \ref alib_enums_records "ALib Enum Records" of (derived) type
114/// \alib{enums;ERSerializable}.
115///
116///
117/// @tparam TEnum The enumeration type equipped with <b>ALib Enum Records</b>
118/// of (derived) type \alib{enums;ERSerializable} and furthermore with a
119/// specialization of \alib{enums;T_EnumIsBitwise}.
120/// @tparam TChar The character type of the \p{input} substring.
121/// Deduced by the compiler.
122/// @tparam TSensitivity The sensitivity of the comparison.
123/// Defaults to \b Case::Ignore.
124/// @tparam TTrimBeforeConsume Determines if the substring should be (left-) trimmed before and
125/// after each consume operation. If so, in case of parsing failure,
126/// trimming is \b not restored.<br>
127/// Defaults to \b Whitespaces::Trim.
128/// @tparam delimiter The delimiter character of the enum elements.<br>
129/// Defaults to <c>','</c>.
130/// @tparam keepLastDelim If \c true , the delimiter will be kept in this substring, if
131/// after the delimiter no further enum element was found.
132/// If \c false, the delimiter will be kept.<br>
133/// Defaults to \c true.
134/// @param[in,out] input The substring to parse. Passed as reference. On success, the
135/// substring will be shortened by the characters consumed. On failure
136/// only trimmed characters are consumed.
137/// @param[out] result The result enum element given as reference.
138/// @return \c true if an enum element was successfuly recognized, \c false otherwise.
139///
140/// ### Module Dependencies ###
141/// This method is only available if \alib_strings is included in the \alibdist.
142//==================================================================================================
143template<typename TEnum,
144 typename TChar,
145 Case TSensitivity = Case::Ignore,
146 Whitespaces TTrimBeforeConsume = Whitespaces::Trim,
147 TChar delimiter = ',',
148 bool keepLastDelim = true >
149inline
150bool ParseBitwise( strings::TSubstring<TChar>& input, TEnum& result );
151#else
152template<typename TEnum,
153 typename TChar,
154 lang::Case TSensitivity = lang::Case::Ignore,
155 lang::Whitespaces TTrimBeforeConsume = lang::Whitespaces::Trim,
156 TChar delimiter = ',',
157 bool keepLastDelim = true >
158ATMP_T_IF(bool, EnumRecords<TEnum>::template AreOfType<ERSerializable>()
160ParseBitwise( strings::TSubstring<TChar>& input, TEnum& result )
161{
162 bool mResult= false;
163 result= TEnum(0);
164 strings::TSubstring<TChar> restoreBeforeDelim;
165 if constexpr ( keepLastDelim )
166 restoreBeforeDelim= input;
167 for(;;)
168 {
169 if constexpr ( TTrimBeforeConsume == lang::Whitespaces::Trim )
170 input.TrimStart();
171 TEnum actEnum;
173 {
174 if constexpr ( keepLastDelim )
175 input= restoreBeforeDelim;
176 return mResult;
177 }
178 result|= actEnum;
179 mResult= true;
180 if constexpr ( TTrimBeforeConsume == lang::Whitespaces::Trim )
181 input.TrimStart();
182 if constexpr ( keepLastDelim )
183 restoreBeforeDelim= input;
184
185 if( !input.template ConsumeChar<TSensitivity, TTrimBeforeConsume>( delimiter ) )
186 return mResult;
187
188 }
189}
190#if defined(_MSC_VER)
191#pragma warning( pop )
192#endif
193
194#endif
195
196#if DOXYGEN
197//==================================================================================================
198/// Convenience method that first uses \alib{enums;Parse} to try and read an element of a C++
199/// enum. If this is not successful, an enum of type \alib{lang;Bool} is tried to be read.
200/// If this is successful, depending on the value read, the \p{TEnum} values given
201/// as parameters \p{falseValue} and \p{trueValue} are assigned.
202/// Otherwise false is returned.
203///
204/// In debug-builds, the method asserts that at least one record is defined for \p{TEnum}.
205///
206/// \see
207/// For more information consult chapter
208/// \ref alib_enums_records_details_serialization of the
209/// Programmer's Manual of module \alib_basecamp.
210///
211/// @tparam TEnum The enumeration type equipped with <b>ALib Enum Records</b>
212/// of (derived) type \alib{enums;ERSerializable}.
213/// @tparam TChar The character type of the \p{input} substring.
214/// Deduced by the compiler.
215/// @tparam TSensitivity The sensitivity of the comparison.
216/// Defaults to \b Case::Ignore.
217/// @tparam TTrimBeforeConsume Determines if the substring should be (left-) trimmed before and
218/// after each consume operation. If so, in case of parsing failure,
219/// trimming is \b not restored.<br>
220/// Defaults to \b Whitespaces::Trim.
221/// @param trueValue The \p{TEnum} value to use in case of \c Bool::True was read.
222/// @param falseValue The \p{TEnum} value to use in case of \c Bool::False was read.
223/// @param[in,out] input The substring to parse. Passed as reference. On success, the
224/// substring will be shortened by the characters consumed. On failure
225/// only trimmed characters are consumed.
226/// @param[out] result The result enum element given as reference.
227/// @return \c true if an element of \p{TEnum} or \alib{lang;Bool} could be read,
228/// \c false otherwise.
229//==================================================================================================
230template<typename TEnum,
231 typename TChar,
232 Case TSensitivity = Case::Ignore,
233 Whitespaces TTrimBeforeConsume = Whitespaces::Trim >
234inline
236 TEnum& result,
237 TEnum falseValue,
238 TEnum trueValue );
239#else
240template<typename TEnum,
241 typename TChar,
242 lang::Case TSensitivity = lang::Case::Ignore,
243 lang::Whitespaces TTrimBeforeConsume = lang::Whitespaces::Trim >
244ATMP_T_IF(bool, EnumRecords<TEnum>::template AreOfType<ERSerializable>() )
245ParseEnumOrTypeBool( strings::TSubstring<TChar>& input,
246 TEnum& result,
247 TEnum falseValue,
248 TEnum trueValue )
249{
250 // first try to read a TEnum
252 return true;
253
254 // if failed, read boolean
255 lang::Bool boolEnum;
257 {
258 result= (boolEnum == lang::Bool::True) ? trueValue : falseValue;
259 return true;
260 }
261
262 // failed
263 return false;
264}
265#endif
266
267}} // namespace [alib::enums]
268
269
270
271// #################################################################################################
272// Writing (T_Append<Enum>)
273// #################################################################################################
274
275namespace alib { namespace strings {
276
277// Faking specializations of T_Append for doxygen into namespace alib::strings::APPENDABLES
278#if DOXYGEN
279 namespace APPENDABLES {
280#endif
281
282
283#if DOXYGEN
284/// Templated specialization of functor \alib{strings;T_Append} makes elements of C++ enumeration
285/// type \p{TEnum} appendable to \alib{strings;TAString;AString} instances, if:
286/// - Type \p{TEnum} is equipped with \ref alib_enums_records "ALib Enum Records" of type
287/// \alib{enums;ERSerializable} (or a derived type ), and if:
288/// - \b No specialization of TMP struct \alib{enums;T_EnumIsBitwise} exists for \p{TEnum}.
289/// (For those, a different implementation of this functor is given.)
290///
291/// \note
292/// The conditions are evaluated by the compiler using \c std::enable_if on optional template
293/// parameter \p{TEnableIf} of unspecialized \alib{strings;T_Append}.
294///
295/// Member \b operator() writes the name of the given enumeration value to \p{target}.
296/// As with all specializations of functor \b %T_Append, the use of it is done implicitly
297/// with various interface method of class \alib{strings;TAString;AString}.
298///
299/// If furthermore TMP struct \alib{lang::resources;T_Resourced} is specialized for type \p{TEnum}
300/// and an externalizd resouce string exists according to the specification described with
301/// methods \alib{lang::resources;ResourcedType::TypeNamePrefix} and
302/// \alib{lang::resources;ResourcedType::TypeNamePostfix} then these resourced strings are written
303/// prior and after the enumeration element's name.
304///
305/// \see
306/// - Sibling specialization
307/// \alib{strings::APPENDABLES;T_Append<TEnumBitwise,TChar,TAllocator>}
308/// - \ref alib_enums_records "ALib Enum Records".
309/// - TMP struct \alib{lang::resources;T_Resourced}.
310/// - Corresponding convenience type \alib{lang::resources;ResourcedType}.
311/// - Some sample code is given with documentation \ref alib_enums_records "ALib Enum Records".
312///
313/// @tparam TEnum The enumeration type of the element that is to be appended to an \b %AString.
314/// @tparam TChar The character type of the target \b %AString.
315/// @tparam TAllocator The allocator type of the target \b %AString, as prototyped with
316/// \alib{lang;Allocator}.
317template<typename TEnum, typename TChar, typename TAllocator>
318struct T_Append<TEnum, TChar, TAllocator>
319#else
320template<typename TEnum, typename TChar, typename TAllocator>
321struct T_Append<TEnum, TChar,TAllocator,
323 && !T_EnumIsBitwise<TEnum>::value ) >
324#endif
325{
326 //==============================================================================================
327 /// Writes the name of the given enumeration \p{element} it to \p{target}.
328 ///
329 /// If available for \p{TEnum}, the resourced name
330 /// \alib{lang::resources;ResourcedType::TypeNamePrefix;prefix} and
331 /// \alib{lang::resources;ResourcedType::TypeNamePostfix;postfix} are written before and after the
332 /// element's name.
333 ///
334 /// If no record exists for \p{element}, its underlying integral value is written.
335 /// In debug-builds, the method asserts that at least one record is defined for \p{TEnum}.
336 ///
337 ///
338 /// @param target The \b AString that \p{element} is to be appended to.
339 /// @param element The enumeration element to append to \p{target}.
340 //==============================================================================================
341 void operator()( TAString<TChar,TAllocator>& target, TEnum element )
342 {
343 ALIB_ASSERT_ERROR( EnumRecords<TEnum>().begin() != EnumRecords<TEnum>().end(), "ENUMS",
344 NString128() << "No Enum Records for type <"
345 << lang::DbgTypeDemangler( typeid(TEnum)).Get() << "> found." )
346
348 auto* record= enums::TryRecord( element );
349 if( record != nullptr )
350 target << record->EnumElementName;
351 else
352 target << UnderlyingIntegral( element );
354 }
355};
356
357
358#if DOXYGEN
359/// Templated specialization of functor \alib{strings;T_Append} makes elements of C++ enumeration
360/// type \p{TEnum} appendable to \alib{strings;TAString;AString} instances, if
361/// - Type \p{TEnum} is equipped with \ref alib_enums_records "ALib Enum Records" of type
362/// \alib{enums;ERSerializable} (or a derived type derived from \b ERSerializable), and
363/// - \b A specialization of TMP struct \alib{enums;T_EnumIsBitwise} exists for \p{TEnum}.
364/// (For non-bitwise types, a different implementation of this functor is given.)
365///
366/// \note
367/// The conditions are evaluated by the compiler using \c std::enable_if on optional template
368/// parameter \p{TEnableIf} of unspecialized \alib{strings;T_Append}.
369///
370/// Member \b operator() writes all value names corresponding to the bits set in \p{src},
371/// separated by delimiter character <c>','</c>.
372/// \note
373/// For technical reasons, the delimiter is not adjustable. In cases a different delimter
374/// is to be used, it needs to be replaced in the string after the value is written.
375///
376/// If the underlying integral value of a given enum element may be become \c 0, a corresponding
377/// enum record associated to such non-bit value will be used if existent.
378///
379/// Furthermore, with bitwise type enums, the defined enum records may contain entries that
380/// represent combinations of more than one integral bit. Such combination entries are supported
381/// but have to be registered with class \alib{enums;EnumRecords} \b before the
382/// standard single bit entries!
383/// If combined bit values are matched, the corresponding element names that represent the
384/// single bit values will not be written.
385///
386/// As a sample, lets consider a window manager software which has a scoped enum type representing
387/// window states. Because a window might have more than one state, macro
388/// \ref ALIB_ENUMS_MAKE_BITWISE is used to specialize TMP struct \alib{enums;T_EnumIsBitwise}.
389/// Next, macro \ref ALIB_ENUMS_ASSIGN_RECORD used to associate the type with enum record type
390/// \alib{enums;ERSerializable}:
391///
392/// \snippet "DOX_ENUMS.cpp" DOX_ENUMS_BITWISE_DECLARATION
393///
394/// A window that is both, vertically and horizontally maximized, is considered to be "just"
395/// \e maximized. Therefore, during bootstrap of the software, enum records that fetch that the
396/// combination of states are defined \b before the single-state records:
397///
398/// \snippet "DOX_ENUMS.cpp" DOX_ENUMS_BITWISE_DEFINITION
399///
400/// That is all that is needed! With this setup, the following code:
401/// \snippet "DOX_ENUMS.cpp" DOX_ENUMS_BITWISE_SAMPLE
402///
403/// produces this output:
404/// \snippet "DOX_ENUMS_BITWISE_OUTPUT.txt" OUTPUT
405///
406/// If furthermore TMP struct \alib{lang::resources;T_Resourced} is specialized for type \p{TEnum}
407/// and an externalizd resouce string exists according to the specification described with
408/// methods \alib{lang::resources;ResourcedType::TypeNamePrefix} and
409/// \alib{lang::resources;ResourcedType::TypeNamePostfix} then these resourced strings are written
410/// prior and after the enumeration element name(s).
411///
412/// \see
413/// - Sibling specialization
414/// \alib{strings::APPENDABLES;T_Append<TEnum,TChar,TAllocator>;T_Append<TEnum,TChar,TAllocator>}
415/// - TMP struct \alib{lang::resources;T_Resourced}.
416/// - Corresponding convenience type \alib{lang::resources;ResourcedType}.
417/// - Some sample code is given with documentation \ref alib_enums_records "ALib Enum Records".
418///
419/// # Reference Documentation #
420/// @tparam TEnumBitwise The bitwise defined enumeration type.
421/// @tparam TChar The character type of the target \b %AString.
422/// @tparam TAllocator The allocator type of the target \b %AString, as prototyped with
423/// \alib{lang;Allocator}.
424template<typename TEnumBitwise, typename TChar>
425struct T_Append<TEnumBitwise, TChar,TAllocator>
426#else
427template<typename TEnum, typename TChar, typename TAllocator>
428struct T_Append<TEnum, TChar,TAllocator,
430 && T_EnumIsBitwise<TEnum>::value )>
431#endif
432{
433 //==============================================================================================
434 /// Writes a comma-separated list of element names of the bitwise defined enumeration \p{TEnum}
435 /// to \p{target}.
436 ///
437 /// In debug-builds, the method asserts that at least one record is defined for \p{TEnum}.
438 /// It is furthermore asserted that all bits contained in \p{elements} have been "covered"
439 /// by corresponding names.
440 ///
441 /// The enum records defined may aggregate several bits. Aggregations have to be defined
442 /// before records that represent the corresponding single bits (or another subset of those).
443 ///
444 /// \see
445 /// This struct's documentation for more information and a sample.
446 ///
447 /// @param target The \b AString that \p{elements} is to be appended to.
448 /// @param elements The enumeration element to append to \p{target}.
449 //==============================================================================================
450 void operator()( TAString<TChar,TAllocator>& target, TEnum elements )
451 {
452 ALIB_ASSERT_ERROR( EnumRecords<TEnum>().begin() != EnumRecords<TEnum>().end(), "ENUMS",
453 NString128() << "No Enum Records for type <"
454 << lang::DbgTypeDemangler( typeid(TEnum)).Get() << "> found." )
455
457
458 // check what has been covered and omit double entries
459 TEnum covered= TEnum(0);
460
461 // loop over entry 2 to end, check bit
462 integer len= target.Length();
463
464 for( auto recordIt= EnumRecords<TEnum>().begin() ;
465 recordIt != EnumRecords<TEnum>().end() ; ++recordIt )
466 {
467 // no bits are set and this entry does not contain bits, then stop here
468 if( recordIt.Integral() == 0 )
469 {
470 if( elements == TEnum(0) )
471 {
472 target << recordIt->EnumElementName;
474 return;
475 }
476 }
477 else if( HasBits( elements, recordIt.Enum() )
478 && !HasBits( covered , recordIt.Enum() ) )
479 {
480 covered|= recordIt.Enum();
481 target << recordIt->EnumElementName << ',';
482 }
483 }
484 len= target.Length() - len;
485
486 // remove the last comma
487 if( len != 0 )
488 target.DeleteEnd( 1 );
489
490 ALIB_ASSERT_ERROR( covered == elements, "ENUMS",
491 NString128() << "Not all bits have been covered while writing bitset '"
492 << NFormat::Bin( elements ) << "' of enumeration type <"
493 << lang::DbgTypeDemangler( typeid(TEnum)).Get() << ">. Remaining bits are '"
494 << NFormat::Bin( covered & elements ) << "'." )
495
497 }
498};
499
500
501#if DOXYGEN
502} // namespace alib::strings[::appendables]
503#endif
504}} // namespace [alib::strings]
505
507
508#endif // HPP_ALIB_ENUMS_SERIALIZATION
509
TAString & DeleteEnd(integer regionLength)
constexpr integer Length() const
Definition string.hpp:326
TSubstring & TrimStart(const TCString< TChar > &whiteSpaces=TT_CStringConstants< TChar >::DefaultWhitespaces())
Definition substring.hpp:89
#define ALIB_ASSERT_MODULE(modulename)
Definition alib.hpp:223
#define ATMP_VOID_IF(Cond)
Definition tmp.hpp:47
#define ATMP_ISOF( T, TBase)
Definition tmp.hpp:28
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:1271
#define IF_ALIB_CAMP(...)
Definition alib.hpp:336
#define ATMP_T_IF(T, Cond)
Definition tmp.hpp:49
bool ParseBitwise(strings::TSubstring< TChar > &input, TEnum &result)
bool Parse(strings::TSubstring< TChar > &input, TEnum &result)
bool ParseEnumOrTypeBool(strings::TSubstring< TChar > &input, TEnum &result, TEnum falseValue, TEnum trueValue)
Whitespaces
Denotes whether a string is trimmed or not.
@ Trim
Trim whitespaces away.
@ True
True value.
Case
Denotes upper and lower case character treatment.
Definition alib.cpp:69
NLocalString< 128 > NString128
Type alias name for TLocalString<nchar,128>.
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:273
static constexpr ForwardIterator end()
Definition records.hpp:423
void operator()(TAString< TChar, TAllocator > &target, TEnum elements)
void operator()(TAString< TChar, TAllocator > &target, TEnum element)