Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
ToString.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include <chrono>
5#include <exception>
6#include <filesystem>
7#include <functional>
8#include <ranges>
9#include <thread>
10#include <typeindex>
11#include <typeinfo>
12#include <variant>
13#include <wchar.h>
14
15#include "Stroika/Foundation/Characters/FloatConversion.h"
18#include "Stroika/Foundation/Common/Concepts.h"
21
22namespace Stroika::Foundation::Time {
23 class Duration;
24}
26
27 namespace Private_ {
28
29 /**
30 * \brief checks if the given type has a .ToString () const method returning a string
31 */
32 template <typename T>
33 concept has_ToStringMethod_v = requires (T t) {
34 { t.ToString () } -> convertible_to<Characters::String>;
35 };
36 /**
37 * \brief this given type appears to be a 'pair' of some sort
38 */
39 template <typename T>
40 concept has_pair_v = requires (T t) {
41 t.first;
42 t.second;
43 };
44 /**
45 * \brief this given type appears to be a 'KeyValuePair' of some sort
46 */
47 template <typename T>
48 concept has_KeyValuePair_v = requires (T t) {
49 t.fKey;
50 t.fValue;
51 };
52 /**
53 * \brief this given type appears to be a 'CountedValue' of some sort
54 */
55 template <typename T>
56 concept has_CountedValue_v = requires (T t) {
57 t.fValue;
58 t.fCount;
59 };
60
61 template <typename T>
62 inline String num2Str_ (T t, ios_base::fmtflags flags)
63 {
64 static_assert (sizeof (t) <= sizeof (int));
65 wchar_t buf[1024];
66 switch (flags) {
67 case ios_base::oct:
68 (void)::swprintf (buf, Memory::NEltsOf (buf), L"%o", t);
69 break;
70 case ios_base::dec:
71 (void)::swprintf (buf, Memory::NEltsOf (buf), L"%d", t);
72 break;
73 case ios_base::hex:
74 (void)::swprintf (buf, Memory::NEltsOf (buf), L"0x%x", t);
75 break;
76 default:
77 AssertNotReached (); // @todo support octal
78 }
79 return buf;
80 }
81 template <typename T>
82 inline String num2Strl_ (T t, ios_base::fmtflags flags)
83 {
84 wchar_t buf[1024];
85 static_assert (sizeof (t) == sizeof (long int));
86 switch (flags) {
87 case ios_base::oct:
88 (void)::swprintf (buf, Memory::NEltsOf (buf), L"%lo", t);
89 break;
90 case ios_base::dec:
91 (void)::swprintf (buf, Memory::NEltsOf (buf), L"%ld", t);
92 break;
93 case ios_base::hex:
94 (void)::swprintf (buf, Memory::NEltsOf (buf), L"0x%lx", t);
95 break;
96 default:
97 AssertNotReached (); // @todo support octal
98 }
99 return buf;
100 }
101 template <typename T>
102 inline String num2Strll_ (T t, ios_base::fmtflags flags)
103 {
104 wchar_t buf[1024];
105 static_assert (sizeof (t) == sizeof (long long int));
106 switch (flags) {
107 case ios_base::oct:
108 (void)::swprintf (buf, Memory::NEltsOf (buf), L"%llo", t);
109 break;
110 case ios_base::dec:
111 (void)::swprintf (buf, Memory::NEltsOf (buf), L"%lld", t);
112 break;
113 case ios_base::hex:
114 (void)::swprintf (buf, Memory::NEltsOf (buf), L"0x%llx", t);
115 break;
116 default:
117 AssertNotReached (); // @todo support octal
118 }
119 return buf;
120 }
121
122 }
123
124 /**
125 * Collect all the default ToString() implementations, templates, overloads etc, all in one namespace.
126 *
127 * Users of the Stroika library may specialize Characters::ToString(), but (???) probably should not add overloads ot the
128 * ToStringDefaults namespace --LGP 2023-11-21.
129 */
130 namespace ToStringDefaults {
131
132 // IN CPP FILE
133 String ToString (const exception_ptr& t);
134 String ToString (const exception& t);
135 String ToString (const type_info& t);
136 String ToString (const type_index& t);
137 String ToString (const thread::id& t);
138 String ToString (bool t);
139
140 template <Private_::has_ToStringMethod_v T>
141 inline String ToString (const T& t)
142 {
143 return t.ToString ();
144 }
145 template <ranges::range T>
146 String ToString (const T& t)
147 requires (not Private_::has_ToStringMethod_v<T> and not is_convertible_v<T, String>)
148 {
149 StringBuilder sb;
150 sb << "["sv;
151 bool didFirst{false};
152 for (const auto& i : t) {
153 if (didFirst) {
154 sb << ", "_k;
155 }
156 else {
157 sb << " "_k;
158 }
159 sb << Characters::ToString (i);
160 didFirst = true;
161 }
162 if (didFirst) {
163 sb << " "_k;
164 }
165 sb << "]"_k;
166 return sb;
167 }
168#if 0
169 // Until Stroika 3.0d6x we did this - but new format specification stuff generally better ":.100" and I think leading <> etc for which to keep? Maybe not - no way to add elipsis, maybe I can add that?
170 // @todo consider - but do differntly than this - so remove and then add somethign in spirit of new format code later
171 template <typename T>
172 inline String ToString (const T& t, StringShorteningPreference shortenPref = StringShorteningPreference::eDEFAULT, size_t maxLen2Display = 100)
173 requires (is_convertible_v<T, String>)
174 {
175 return "'"sv + static_cast<String> (t).LimitLength (maxLen2Display, shortenPref) + "'"sv;
176 }
177#endif
178 template <typename T>
179 inline String ToString (const T& t)
180 requires (is_convertible_v<T, String>)
181 {
182 return static_cast<String> (t);
183 }
184 template <typename T>
185 inline String ToString ([[maybe_unused]] const T& t)
186 requires (is_convertible_v<T, tuple<>>)
187 {
188 return "{}"sv;
189 }
190 template <IToString T1>
191 inline String ToString (const atomic<T1>& t)
192 {
193 return Characters::ToString (t.load ());
194 }
195 template <typename T1>
196 String ToString (const tuple<T1>& t)
197 {
198 StringBuilder sb;
199 sb << "{"sv << Characters::ToString (get<0> (t)) << "}"sv;
200 return sb;
201 }
202 template <typename T1, typename T2>
203 String ToString (const tuple<T1, T2>& t)
204 {
205 StringBuilder sb;
206 sb << "{"sv << Characters::ToString (get<0> (t)) << ", "sv << Characters::ToString (get<1> (t)) << "}"sv;
207 return sb;
208 }
209 template <typename T1, typename T2, typename T3>
210 String ToString (const tuple<T1, T2, T3>& t)
211 {
212 StringBuilder sb;
213 sb << "{"sv;
214 sb << Characters::ToString (get<0> (t)) << ", "sv << Characters::ToString (get<1> (t)) << ", "sv << Characters::ToString (get<2> (t));
215 sb << "}"sv;
216 return sb;
217 }
218 template <typename T1, typename T2, typename T3, typename T4>
219 String ToString (const tuple<T1, T2, T3>& t)
220 {
221 StringBuilder sb;
222 sb << "{"sv;
223 sb << Characters::ToString (get<0> (t)) << ", "sv << Characters::ToString (get<1> (t))
224 << ", "sv + Characters::ToString (get<2> (t)) << ", "sv << Characters::ToString (get<3> (t));
225 sb << "}"sv;
226 return sb;
227 }
228 template <typename... TYPES>
229 String ToString (const variant<TYPES...>& v)
230 {
231 // @todo WRITE THIS BETTER?
232 // Want to just write - return ToString (get<v.index ()> (v));
233 // But not sure how???
234 if constexpr (sizeof...(TYPES) > 0) {
235 if (v.index () == 0) {
236 return ToString (get<0> (v));
237 }
238 }
239 if constexpr (sizeof...(TYPES) > 1) {
240 if (v.index () == 1) {
241 return ToString (get<1> (v));
242 }
243 }
244 if constexpr (sizeof...(TYPES) > 2) {
245 if (v.index () == 2) {
246 return ToString (get<2> (v));
247 }
248 }
249 if constexpr (sizeof...(TYPES) > 3) {
250 if (v.index () == 3) {
251 return ToString (get<3> (v));
252 }
253 }
254 if constexpr (sizeof...(TYPES) > 4) {
255 if (v.index () == 4) {
256 return ToString (get<4> (v));
257 }
258 }
259 if constexpr (sizeof...(TYPES) > 5) {
260 if (v.index () == 5) {
261 return ToString (get<5> (v));
262 }
263 }
264 if constexpr (sizeof...(TYPES) > 6) {
265 if (v.index () == 6) {
266 return ToString (get<6> (v));
267 }
268 }
269 if constexpr (sizeof...(TYPES) > 7) {
270 if (v.index () == 7) {
271 return ToString (get<7> (v));
272 }
273 }
274 if constexpr (sizeof...(TYPES) > 8) {
275 if (v.index () == 8) {
276 return ToString (get<8> (v));
277 }
278 }
279 if constexpr (sizeof...(TYPES) > 9) {
280 if (v.index () == 9) {
281 return ToString (get<9> (v));
282 }
283 }
285 return String{};
286 }
287 template <typename T>
288 String ToString (const T& t)
289 requires (Private_::has_pair_v<T>)
290 {
291 StringBuilder sb;
292 sb << "{"sv;
293 sb << Characters::ToString (t.first) << ": "sv << Characters::ToString (t.second);
294 sb << "}"sv;
295 return sb;
296 }
297 template <Private_::has_KeyValuePair_v T>
298 String ToString (const T& t)
299 {
300 StringBuilder sb;
301 sb << "{"sv;
302 sb << Characters::ToString (t.fKey) << ": "sv << Characters::ToString (t.fValue);
303 sb << "}"sv;
304 return sb;
305 }
306 template <Private_::has_CountedValue_v T>
307 String ToString (const T& t)
308 {
309 StringBuilder sb;
310 sb << "{"sv;
311 sb << "'" << Characters::ToString (t.fValue) << "': "sv << Characters::ToString (t.fCount);
312 sb << "}"sv;
313 return sb;
314 }
315 template <typename T>
316 inline String ToString (const T& t)
317 requires (is_enum_v<T>)
318 {
319 if constexpr (not Common::IBoundedEnum<T>) {
320 return Characters::ToString (underlying_type_t<T> (t));
321 }
322 else if constexpr (Common::DefaultNames<T>{}.size () == 0) {
323 // emit as number if no EnumNames<> declared
324 return Characters::ToString (underlying_type_t<T> (t));
325 }
326 else {
327 return Common::DefaultNames<T>{}.GetName (t);
328 }
329 }
330 template <typename OPTIONS>
331 inline String ToString (const StringBuilder<OPTIONS>& sb)
332 {
333 return sb.template As<String> ();
334 }
335 template <floating_point T>
336 inline String ToString (T t)
337 {
338 return FloatConversion::ToString (t);
339 }
340 template <floating_point T>
341 inline String ToString (T t, FloatConversion::ToStringOptions o)
342 {
343 return FloatConversion::ToString (t, o);
344 }
345 template <typename T>
346 inline String ToString (const Math::CommonStatistics<T>& t)
347 {
349
350 sb << "{"sv;
351 if (t.fMin) {
352 sb << "min: " << *t.fMin;
353 }
354 if (t.fMax) {
355 sb << "max: " << *t.fMax;
356 }
357 if (t.fMean) {
358 sb << "mean: " << *t.fMean;
359 }
360 if (t.fMedian) {
361 sb << "median: " << *t.fMedian;
362 }
363 if (t.fStandardDeviation) {
364 sb << "standard-deviation: " << *t.fStandardDeviation;
365 };
366 sb << "}"sv;
367 return sb;
368 }
369 template <typename T>
370 inline String ToString (const shared_ptr<T>& pt)
371 {
372 if (pt == nullptr) {
373 return "nullptr"sv;
374 }
375 if constexpr (IToString<decltype (*pt)>) {
376 return String{Common::StdCompat::format (L"*{}", Characters::ToString (*pt))};
377 }
378 return String{Common::StdCompat::format (L"{}", static_cast<const void*> (pt.get ()))};
379 }
380 template <typename T>
381 inline String ToString (const unique_ptr<T>& pt)
382 {
383 if (pt == nullptr) {
384 return "nullptr"sv;
385 }
386 if constexpr (IToString<decltype (*pt)>) {
387 return String{Common::StdCompat::format (L"*{}", Characters::ToString (*pt))};
388 }
389 return String{Common::StdCompat::format (L"{}", static_cast<const void*> (pt.get ()))};
390 }
391 template <typename T>
392 inline String ToString (const optional<T>& o)
393 {
394 return o.has_value () ? Characters::ToString (*o) : "[missing]"sv;
395 }
396 template <typename FUNCTION_SIGNATURE>
397 inline String ToString (const function<FUNCTION_SIGNATURE>& f)
398 {
399 return Common::StdCompat::format (L"{}", static_cast<const void*> (f.template target<remove_cvref_t<FUNCTION_SIGNATURE>> ()));
400 }
401 inline String ToString (const chrono::duration<double>& t)
402 {
403 return Characters::ToString (t.count ()) + " seconds"sv;
404 }
405 String ToString (const Time::Duration& t, FloatConversion::Precision p);
406 template <typename CLOCK_T>
407 inline String ToString (const chrono::time_point<CLOCK_T, chrono::duration<double>>& t)
408 {
409 return Characters::ToString (t.time_since_epoch ().count ()) + " seconds"sv;
410 }
411 template <integral T>
412 inline String ToString (T t, ios_base::fmtflags flags)
413 {
414 using namespace Private_;
415 if constexpr (sizeof (T) < sizeof (long)) {
416 return num2Str_ (t, flags);
417 }
418 else if constexpr (sizeof (T) == sizeof (long)) {
419 return num2Strl_ (t, flags);
420 }
421 else if constexpr (sizeof (T) == sizeof (long long int)) {
422 return num2Strll_ (t, flags);
423 }
424 }
425 template <signed_integral T>
426 inline String ToString (T t)
427 {
428 return Characters::ToString (t, ios_base::dec);
429 }
430 template <unsigned_integral T>
431 inline String ToString (T t)
432 {
433 // no overwhelmingly good reason todo it this way, but this matches what we had in Stroika 2.1, and its reasonable...
434 if constexpr (sizeof (T) == 1) {
435 return Characters::ToString (t, ios_base::hex);
436 }
437 else {
438 return Characters::ToString (t, ios_base::dec);
439 }
440 }
441 inline String ToString (byte t)
442 {
443 return Characters::ToString (static_cast<unsigned char> (t), ios_base::hex);
444 }
445#if 0
446 inline String ToString (const filesystem::path& t, StringShorteningPreference shortenPref = StringShorteningPreference::ePreferKeepRight,
447 size_t maxLen2Display = 100)
448 {
449 return Characters::ToString (t.wstring (), shortenPref, maxLen2Display); // wrap in 'ToString' for surrounding quotes
450 }
451#endif
452 inline String ToString (const filesystem::path& t)
453 {
454 return t.wstring ();
455 }
456
457 }
458
459 /*
460 ********************************************************************************
461 *************************** Characters::ToString *******************************
462 ********************************************************************************
463 */
464 template <typename T, typename... ARGS>
465 inline String ToString (T&& t, ARGS... args)
466 {
467 return ToStringDefaults::ToString (forward<T> (t), forward<ARGS> (args)...);
468 }
469
470 /*
471 ********************************************************************************
472 *********************** Characters::UnoverloadedToString ***********************
473 ********************************************************************************
474 */
475 template <typename T>
476 inline String UnoverloadedToString (const T& t)
477 {
478 return ToString (t);
479 }
480
481 //DEPRECATED
482 namespace Private_ {
483 template <typename T>
484 using has_ToString_t = decltype (static_cast<Characters::String> (declval<T&> ().ToString ()));
485 }
486 /*
487 * \brief Return true iff Characters::ToString (T) is well defined.
488 */
489 template <typename T>
490 [[deprecated ("Since Stroika v3.0d5 use IToString")]] constexpr inline bool has_ToString_v =
491 Common::is_detected_v<Private_::has_ToString_t, T>;
492
493}
494
495namespace Stroika::Foundation::Traversal {
496#if qCompilerAndStdLib_template_optionalDeclareIncompleteType_Buggy
497 template <typename T>
498 inline Characters::String Iterable<T>::Join (const Characters::String& separator) const
499 {
500 return Join (separator, nullopt);
501 }
502 template <typename T>
503 template <typename RESULT_T, invocable<T> CONVERT_TO_RESULT>
504 inline RESULT_T Iterable<T>::Join (const CONVERT_TO_RESULT& convertToResult, const RESULT_T& separator) const
505 requires (convertible_to<invoke_result_t<CONVERT_TO_RESULT, T>, RESULT_T>)
506 {
507 return Join<RESULT_T> (convertToResult, separator, nullopt);
508 }
509#endif
510 template <typename T>
511 inline Characters::String Iterable<T>::Join (const Characters::String& separator, const optional<Characters::String>& finalSeparator) const
512 {
513 using namespace Characters;
514#if qCompilerAndStdLib_kDefaultToStringConverter_Buggy
515 function<String (T)> cvt;
516 if constexpr (same_as<T, String>) {
517 cvt = Common::Identity{};
518 }
519 else {
520 cvt = UnoverloadedToString<T>;
521 }
522 return this->Join (cvt, StringCombiner<String>{.fSeparator = separator, .fSpecialSeparatorForLastPair = finalSeparator});
523#else
524 return this->Join (kDefaultToStringConverter<String>,
525 StringCombiner<String>{.fSeparator = separator, .fSpecialSeparatorForLastPair = finalSeparator});
526#endif
527 }
528 template <typename T>
529 template <typename RESULT_T, invocable<T> CONVERT_TO_RESULT>
530 inline RESULT_T Iterable<T>::Join (const CONVERT_TO_RESULT& convertToResult, const RESULT_T& separator, const optional<RESULT_T>& finalSeparator) const
531 requires (convertible_to<invoke_result_t<CONVERT_TO_RESULT, T>, RESULT_T>)
532 {
533 return this->Join<RESULT_T> (
534 convertToResult, Characters::StringCombiner<RESULT_T>{.fSeparator = separator, .fSpecialSeparatorForLastPair = finalSeparator});
535 }
536
537}
#define AssertNotReached()
Definition Assertions.h:355
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
nonvirtual RESULT_T Join(const CONVERT_TO_RESULT &convertToResult=kDefaultToStringConverter<>, const COMBINER &combiner=Characters::kDefaultStringCombiner) const
ape the JavaScript/python 'join' function - take the parts of 'this' iterable and combine them into a...
Check if legal to call Characters::ToString(T)...
Definition ToString.h:120
this given type appears to be a 'CountedValue' of some sort
Definition ToString.inl:56
this given type appears to be a 'KeyValuePair' of some sort
Definition ToString.inl:48
checks if the given type has a .ToString () const method returning a string
Definition ToString.inl:33
this given type appears to be a 'pair' of some sort
Definition ToString.inl:40
STRING_TYPE ToString(FLOAT_TYPE f, const ToStringOptions &options={})
String UnoverloadedToString(const T &t)
same as ToString()/1 - but without the potentially confusing multi-arg overloads (confused some templ...
Definition ToString.inl:476
String ToString(T &&t, ARGS... args)
Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except fo...
Definition ToString.inl:465