4#ifndef _Stroika_Foundation_Characters_ToString_h_
5#define _Stroika_Foundation_Characters_ToString_h_ 1
7#include "Stroika/Foundation/StroikaPreComp.h"
22#include "Stroika/Foundation/Common/Concepts.h"
23#include "Stroika/Foundation/Common/CountedValue.h"
24#include "Stroika/Foundation/Common/KeyValuePair.h"
27#if __cpp_lib_stacktrace >= 202011 && !qCompiler_clangNotCompatibleWithLibStdCPPStackTrace_Buggy
41#if qStroika_HasComponent_fmtlib && (FMT_VERSION >= 110000)
48namespace Stroika::Foundation::Memory {
113 template <
typename T,
typename... ARGS>
114 String
ToString (T&& t, ARGS... args);
119 template <
typename T>
121 {
ToString (t) } -> convertible_to<Characters::String>;
127 template <
typename T>
136 template <IToString T>
138 qStroika_Foundation_Characters_FMT_PREFIX_::formatter<String, wchar_t> fDelegate2_;
140 template <
class ParseContext>
141 constexpr typename ParseContext::iterator parse (ParseContext& ctx)
143 return fDelegate2_.parse (ctx);
146 template <
class FmtContext>
147 typename FmtContext::iterator format (T s, FmtContext& ctx)
const
152 std::wstringstream out;
154#if __cpp_lib_ranges >= 202207L
155 return std::ranges::copy (std::move (out).str (), ctx.out ()).out;
157 return Common::StdCompat::format_to (ctx.out (), L
"{}",
String{out.str ()});
162 template <IToString T>
163 struct ToStringFormatterASCII {
166 template <
class ParseContext>
167 constexpr typename ParseContext::iterator parse (ParseContext& ctx)
170 auto it = ctx.begin ();
171 while (it != ctx.end ()) {
174 if (it == ctx.end ()) {
175 throw Common::StdCompat::format_error{
"Invalid format args (missing }) for ToStringFormatterASCII."};
185 template <
class FmtContext>
186 typename FmtContext::iterator format (T s, FmtContext& ctx)
const
189 std::wstringstream out;
193#if __cpp_lib_ranges >= 202207L
203namespace Stroika::Foundation::Characters::Private_ {
218 template <
typename T>
222#if __cplusplus == 202302L && _GLIBCXX_RELEASE == 15
229 or
requires { []<
typename TRAITS,
typename ALLOCATOR> (type_identity<std::basic_string<char, TRAITS, ALLOCATOR>>) {}(type_identity<T> ()); }
230 or
requires { []<
typename TRAITS,
typename ALLOCATOR> (type_identity<std::basic_string<wchar_t, TRAITS, ALLOCATOR>>) {}(type_identity<T> ()); }
231 or
requires { []<
typename TRAITS> (type_identity<std::basic_string_view<char, TRAITS>>) {}(type_identity<T> ()); }
232 or
requires { []<
typename TRAITS> (type_identity<std::basic_string_view<wchar_t, TRAITS>>) {}(type_identity<T> ()); }
233 or
requires { []<
size_t N> (type_identity<wchar_t[N]>) {}(type_identity<T> ()); }
234 or std::is_arithmetic_v<T>
235#if defined(__GNUC__) && _GLIBCXX_RELEASE >= 15
236 or Common::IAnyOf<remove_cvref_t<T>, _Float32, _Float64>
238 or Common::IAnyOf<decay_t<T>, nullptr_t,
void*,
const void*>
240 or Common::IDuration<T>
241 or
requires { []<
typename DURATION> (type_identity<std::chrono::sys_time<DURATION>>) {}(type_identity<T> ()); }
242#if !defined(_LIBCPP_VERSION) or _LIBCPP_VERSION > 229999
243 or
requires { []<
typename DURATION> (type_identity<std::chrono::utc_time<DURATION>>) {}(type_identity<T> ()); }
244 or
requires { []<
typename DURATION> (type_identity<std::chrono::tai_time<DURATION>>) {}(type_identity<T> ()); }
245 or
requires { []<
typename DURATION> (type_identity<std::chrono::gps_time<DURATION>>) {}(type_identity<T> ()); }
247 or
requires { []<
typename DURATION> (type_identity<std::chrono::file_time<DURATION>>) {}(type_identity<T> ()); }
248 or
requires { []<
typename DURATION> (type_identity<std::chrono::local_time<DURATION>>) {}(type_identity<T> ()); }
249 or Common::IAnyOf<decay_t<T>, chrono::day, chrono::month, chrono::year,
250 chrono::weekday, chrono::weekday_indexed, chrono::weekday_last,
251 chrono::month_day, chrono::month_day_last, chrono::month_weekday, chrono::month_weekday_last,
252 chrono::year_month, chrono::year_month_day, chrono::year_month_day_last, chrono::year_month_weekday,chrono::year_month_weekday_last
253#if (not defined (_GLIBCXX_RELEASE) or _GLIBCXX_RELEASE > 12) and (!defined(_LIBCPP_VERSION) or _LIBCPP_VERSION > 229999)
254 , chrono::sys_info, chrono::local_info
257 or
requires { []<
typename DURATION> (type_identity<chrono::hh_mm_ss<DURATION>>) {}(type_identity<T> ()); }
258#if (not defined (_GLIBCXX_RELEASE) or _GLIBCXX_RELEASE > 12) and (!defined(_LIBCPP_VERSION) or _LIBCPP_VERSION > 229999)
259 or
requires { []<
typename DURATION,
typename TimeZonePtr> (type_identity<chrono::zoned_time<DURATION, TimeZonePtr>>) {}(type_identity<T> ()); }
269#if __cpp_lib_format_ranges
270 or ranges::range<decay_t<T>>
272#if (__cplusplus > 202101L or _LIBCPP_STD_VER >= 23 or (_MSVC_LANG >= 202004 and _MSC_VER >= _MSC_VER_2k22_17Pt11_)) and not (defined (_GLIBCXX_RELEASE) and _GLIBCXX_RELEASE <= 14)
273 or Common::IPair<remove_cvref_t<T>> or Common::ITuple<remove_cvref_t<T>>
275#if __cplusplus > 202101L or _LIBCPP_STD_VER >= 23 or _MSVC_LANG >= 202004
276 or Common::IAnyOf<remove_cvref_t<T>, thread::id>
278#if __cpp_lib_stacktrace >= 202011 && !qCompiler_clangNotCompatibleWithLibStdCPPStackTrace_Buggy
279 or Common::IAnyOf<remove_cvref_t<T>, stacktrace_entry>
280 or
requires { []<
typename ALLOCATOR> (type_identity<basic_stacktrace<ALLOCATOR>>) {}(type_identity<T> ()); }
282#if __cplusplus > 202101L or _LIBCPP_STD_VER >= 23 or _MSVC_LANG >= 202004
283 or
requires { []<
typename TT> (type_identity<stack<TT>>) {}(type_identity<T> ()); }
284 or
requires { []<
typename TT> (type_identity<queue<TT>>) {}(type_identity<T> ()); }
288#if __cplusplus > 202400L or _LIBCPP_STD_VER >= 26 or _MSVC_LANG >= 202400L
290 or Common::IAnyOf<remove_cvref_t<T>, std::filesystem::path>
297#if qStroika_HasComponent_fmtlib
298 or Common::IAnyOf<decay_t<T>, qStroika_Foundation_Characters_FMT_PREFIX_::string_view, qStroika_Foundation_Characters_FMT_PREFIX_::wstring_view>
299#if (FMT_VERSION >= 110000)
303 or Common::IPair<remove_cvref_t<T>> or Common::ITuple<remove_cvref_t<T>>
311#if _MSVC_LANG == 202004 && (_MSC_VER < _MSC_VER_2k22_17Pt11_)
312 static_assert (not IStdFormatterPredefinedFor_<std::pair<int, char>>);
313 static_assert (not IStdFormatterPredefinedFor_<std::tuple<int>>);
314 static_assert (IStdFormatterPredefinedFor_<std::thread::id>);
315 static_assert (not IStdFormatterPredefinedFor_<std::type_index>);
316 static_assert (not IStdFormatterPredefinedFor_<std::exception_ptr>);
318#if _MSVC_LANG == 202004 && (_MSC_VER == _MSC_VER_2k22_17Pt11_)
319 static_assert (IStdFormatterPredefinedFor_<std::pair<int, char>>);
320 static_assert (IStdFormatterPredefinedFor_<std::tuple<int>>);
321 static_assert (IStdFormatterPredefinedFor_<std::thread::id>);
322 static_assert (not IStdFormatterPredefinedFor_<std::type_index>);
323 static_assert (not IStdFormatterPredefinedFor_<std::exception_ptr>);
325#if __cplusplus == 202002L && _GLIBCXX_RELEASE == 13
326 static_assert (not IStdFormatterPredefinedFor_<std::pair<int, char>>);
327 static_assert (not IStdFormatterPredefinedFor_<std::tuple<int>>);
328 static_assert (not IStdFormatterPredefinedFor_<std::thread::id>);
329 static_assert (not IStdFormatterPredefinedFor_<std::type_index>);
330 static_assert (not IStdFormatterPredefinedFor_<std::exception_ptr>);
332#if __cplusplus == 202002L && _GLIBCXX_RELEASE == 14
333 static_assert (not IStdFormatterPredefinedFor_<std::pair<int, char>>);
334 static_assert (not IStdFormatterPredefinedFor_<std::tuple<int>>);
335 static_assert (not IStdFormatterPredefinedFor_<std::thread::id>);
336 static_assert (not IStdFormatterPredefinedFor_<std::type_index>);
337 static_assert (not IStdFormatterPredefinedFor_<std::exception_ptr>);
339#if __cplusplus == 202302L && _GLIBCXX_RELEASE == 14
340 static_assert (not IStdFormatterPredefinedFor_<std::pair<int, char>>);
341 static_assert (not IStdFormatterPredefinedFor_<std::tuple<int>>);
342 static_assert (IStdFormatterPredefinedFor_<std::thread::id>);
343 static_assert (not IStdFormatterPredefinedFor_<std::type_index>);
344 static_assert (not IStdFormatterPredefinedFor_<std::exception_ptr>);
346#if __cplusplus == 202002L && _GLIBCXX_RELEASE == 15
347 static_assert (not IStdFormatterPredefinedFor_<std::filesystem::path>);
348 static_assert (not IStdFormatterPredefinedFor_<std::pair<int, char>>);
349 static_assert (not IStdFormatterPredefinedFor_<std::tuple<int>>);
350 static_assert (not IStdFormatterPredefinedFor_<std::thread::id>);
351 static_assert (not IStdFormatterPredefinedFor_<std::type_index>);
352 static_assert (not IStdFormatterPredefinedFor_<std::exception_ptr>);
354#if __cplusplus == 202302L && _GLIBCXX_RELEASE == 15
355 static_assert (not IStdFormatterPredefinedFor_<std::filesystem::path>);
356 static_assert (IStdFormatterPredefinedFor_<std::pair<int, char>>);
357 static_assert (IStdFormatterPredefinedFor_<std::tuple<int>>);
358 static_assert (IStdFormatterPredefinedFor_<std::thread::id>);
359 static_assert (not IStdFormatterPredefinedFor_<std::type_index>);
360 static_assert (not IStdFormatterPredefinedFor_<std::exception_ptr>);
362#if __cplusplus == 202002L && _GLIBCXX_RELEASE == 16
363 static_assert (not IStdFormatterPredefinedFor_<std::filesystem::path>);
364 static_assert (not IStdFormatterPredefinedFor_<std::pair<int, char>>);
365 static_assert (not IStdFormatterPredefinedFor_<std::tuple<int>>);
366 static_assert (not IStdFormatterPredefinedFor_<std::thread::id>);
367 static_assert (not IStdFormatterPredefinedFor_<std::type_index>);
368 static_assert (not IStdFormatterPredefinedFor_<std::exception_ptr>);
370#if __cplusplus == 202302L && _GLIBCXX_RELEASE == 16
371 static_assert (IStdFormatterPredefinedFor_<std::filesystem::path>);
372 static_assert (IStdFormatterPredefinedFor_<std::pair<int, char>>);
373 static_assert (IStdFormatterPredefinedFor_<std::tuple<int>>);
374 static_assert (IStdFormatterPredefinedFor_<std::thread::id>);
375 static_assert (not IStdFormatterPredefinedFor_<std::type_index>);
376 static_assert (not IStdFormatterPredefinedFor_<std::exception_ptr>);
378#if defined(__APPLE__) && __clang_major__ == 15
379 static_assert (not IStdFormatterPredefinedFor_<std::pair<int, char>>);
380 static_assert (not IStdFormatterPredefinedFor_<std::tuple<int>>);
381 static_assert (not IStdFormatterPredefinedFor_<std::thread::id>);
382 static_assert (not IStdFormatterPredefinedFor_<std::type_index>);
383 static_assert (not IStdFormatterPredefinedFor_<std::exception_ptr>);
397 static_assert (IStdFormatterPredefinedFor_<std::type_index> == Common::StdCompat::formattable<std::type_index, wchar_t>);
398 static_assert (IStdFormatterPredefinedFor_<std::pair<int, char>> == Common::StdCompat::formattable<std::pair<int, char>,
wchar_t>);
399 static_assert (IStdFormatterPredefinedFor_<std::tuple<int>> == Common::StdCompat::formattable<std::tuple<int>,
wchar_t>);
400 static_assert (IStdFormatterPredefinedFor_<std::thread::id> == Common::StdCompat::formattable<std::thread::id, wchar_t>);
401 static_assert (IStdFormatterPredefinedFor_<std::filesystem::path> == Common::StdCompat::formattable<std::filesystem::path, wchar_t>);
402 static_assert (IStdFormatterPredefinedFor_<std::exception_ptr> == Common::StdCompat::formattable<std::exception_ptr, wchar_t>);
411 template <
typename T>
417#if !qCompiler_IUseToStringFormatterForFormatter_Buggy
421 and (
requires (T t) {
422 { t.ToString () } -> convertible_to<Characters::String>;
423 } or Common::IKeyValuePair<remove_cvref_t<T>> or Common::ICountedValue<remove_cvref_t<T>>
425#if !__cpp_lib_format_ranges
426#if !qStroika_HasComponent_fmtlib or (FMT_VERSION < 110000)
427 or (ranges::range<decay_t<T>> and
428 not
Common::IAnyOf<decay_t<T>,
string, wstring, string_view, wstring_view,
const char[],
const wchar_t[],
429 qStroika_Foundation_Characters_FMT_PREFIX_::string_view, qStroika_Foundation_Characters_FMT_PREFIX_::wstring_view>)
432#if _MSC_VER || __cplusplus < 202101L || (__clang__ != 0 && __GLIBCXX__ != 0 && __GLIBCXX__ <= 20240908) || \
433 (!defined(__clang__) && __cplusplus == 202302L && __GLIBCXX__ <= 20240908) and (!defined(_LIBCPP_STD_VER) || _LIBCPP_STD_VER < 23)
434#if !qStroika_HasComponent_fmtlib or (FMT_VERSION < 110000)
440#if (!defined(__cpp_lib_formatters) || __cpp_lib_formatters < 202302L) and (!defined(_LIBCPP_STD_VER) || _LIBCPP_STD_VER < 23)
444#if __cplusplus < 202400L || (defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE <= 14)
448 same_as<T, std::chrono::time_point<chrono::steady_clock, chrono::duration<double>>> or
465template <Stroika::Foundation::Characters::Private_::IUseToStringFormatterForFormatter_ T>
467template <Stroika::Foundation::Characters::Private_::IUseToStringFormatterForFormatter_ T>
468struct qStroika_Foundation_Characters_FMT_PREFIX_::formatter<T, char> : Stroika::Foundation::Characters::ToStringFormatterASCII<T> {};
470#if qCompilerAndStdLib_StdFmtOfPath_Buggy
472struct qStroika_Foundation_Characters_FMT_PREFIX_::formatter<
std::filesystem::path, wchar_t>
475struct qStroika_Foundation_Characters_FMT_PREFIX_::formatter<
std::filesystem::path, char>
476 : Stroika::Foundation::Characters::ToStringFormatterASCII<std::filesystem::path> {};
484static_assert (Stroika::Foundation::Common::StdCompat::formattable<std::exception_ptr, wchar_t>);
485static_assert (Stroika::Foundation::Common::StdCompat::formattable<std::filesystem::path, wchar_t>);
486static_assert (Stroika::Foundation::Common::StdCompat::formattable<std::optional<int>,
wchar_t>);
487static_assert (Stroika::Foundation::Common::StdCompat::formattable<std::shared_ptr<int>,
wchar_t>);
488static_assert (Stroika::Foundation::Common::StdCompat::formattable<std::pair<int, char>,
wchar_t>);
489static_assert (Stroika::Foundation::Common::StdCompat::formattable<std::thread::id, wchar_t>);
490static_assert (Stroika::Foundation::Common::StdCompat::formattable<std::type_index, wchar_t>);
491#if !qCompilerAndStdLib_FormatThreadId_Buggy
492static_assert (Stroika::Foundation::Common::StdCompat::formattable<std::thread::id, wchar_t>);
494#if qCompilerAndStdLib_formattable_of_tuple_Buggy
497static_assert (Stroika::Foundation::Common::StdCompat::formattable<std::tuple<int>,
wchar_t>);
509#include "ToString.inl"
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual string AsNarrowSDKString() const
Check if legal to call Characters::ToString(T)...
concept - trivial shorthand for variadic same_as A or same_as B, or ...
return true iff argument type T, is std::pair<a,b> for some a/b types
return true iff argument type T, is std::shared_ptr<A> for some A types
Concept ITuple<T> check if T is a tuple.
detect if T is a std::variant<> type.
String UnoverloadedToString(const T &t)
same as ToString()/1 - but without the potentially confusing multi-arg overloads (confused some templ...
String ToString(T &&t, ARGS... args)
Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except fo...