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