Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
ToString.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Characters_ToString_h_
5#define _Stroika_Foundation_Characters_ToString_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <chrono>
10#include <ios>
11#include <optional>
12#include <queue>
13#include <sstream>
14#include <stack>
15#include <thread>
16#include <tuple>
17#include <typeindex>
18#include <typeinfo>
19#include <utility>
20#if defined(__cpp_lib_format_ranges)
21#include <format> // std::format_kind, which Stroika specializes for its own ranges
22#endif
23
25#include "Stroika/Foundation/Common/Concepts.h"
26#include "Stroika/Foundation/Common/CountedValue.h"
27#include "Stroika/Foundation/Common/KeyValuePair.h"
29
30#if defined(__cpp_lib_stacktrace)
31#include <stacktrace>
32#endif
33
34/**
35 * \file
36 * Convert arbitrary objects to String form, for the purpose of debugging (not reversible).
37 *
38 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
39 *
40 * TODO:
41 * @todo ToString(tuple) should use variadic templates and support multiple (past 4) args
42 */
43
45
46 /**
47 * \brief Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except for debugging
48 *
49 * Convert an instance of the given object to a printable string representation. This representation
50 * is not guaranteed, pretty, or parsable. This feature is generally for debugging purposes, but can be used
51 * to render/emit objects in any informal setting where you just need a rough sense of the object (again,
52 * the only case I can think of here would be for debugging).
53 *
54 * \note *AKA*
55 * o DUMP
56 * o PrettyPrint
57 *
58 * Patterned after:
59 * Java:
60 * From the Object.toString() docs:
61 * Returns a string representation of the object. In general, the toString method
62 * returns a string that "textually represents" this object.
63 * Javascript:
64 * The toString() method returns a string representing object.
65 * Every object has a toString() method that is automatically called when the object is
66 * to be represented as a text value or when an object is referred to in a manner in which
67 * a string is expected.
68 *
69 * \note Built-in or std types intrinsically supported:
70 * o type_index, type_traits, or anything with a .name () which returns a const SDKChar* string.
71 * o is_array<T>
72 * o is_enum<T>
73 * o atomic<T> where T is a ToStringable type
74 * o shared_ptr<T> (but not unique_ptr<> because its not regular, which std::format requires)
75 * o std::exception
76 * o std::tuple
77 * o std::variant
78 * o std::pair
79 * o std::optional
80 * o std::wstring
81 * o std::filesystem::path
82 * o exception_ptr
83 * o POD types (int, bool, double, etc)
84 * o raw pointers - the address, as std::format prints a void* (0x...); but a character pointer is a C string
85 * o anything with .begin (), .end () - so any container/iterable
86 * o anything class(or struct) with a ToString () method
87 *
88 * \note Other types automatically supported
89 * o KeyValuePair
90 * o CountedValue
91 * o String -- printed as 'the-string' (possibly length limited)
92 *
93 * \note Extra arguments/overloads
94 * o ToString (float, FloatConversion::ToStringOptions) // any floating point argument
95 * o ToString (float, FloatConversion::SignificantFigures) // ''
96 * o ToString (integral T, ios_base::fmtflags flags); // flags may be std::dec, std::oct, or std::hex
97 * // @see https://en.cppreference.com/w/cpp/io/ios_base/fmtflags
98 * o ToString (Duration, FloatConversion::SignificantFigures) // forwards to Duration::As<String> (SignificantFigures)
99 *
100 * \note *Implementation Note*
101 * This implementation defaults to calling T{}.ToString ().
102 *
103 * \note @see IToString to check if Characters::ToString () well defined.
104 */
105 template <typename T, typename... ARGS>
106 String ToString (T&& t, ARGS... args);
107
108 namespace Private_ {
109 template <typename T>
110 struct HasToStringDefault_; // defined in ToString.inl, after every ToStringDefaults overload
111 }
112
113 /**
114 * \brief Check if legal to call Characters::ToString(T)...
115 *
116 * True just where a ToStringDefaults overload takes a T. Not requires { ToString (t) }: the dispatcher above takes any type
117 * (it is its body that fails), so that was true of everything. And not tested here, where none of the overloads is declared yet.
118 */
119 template <typename T>
120 concept IToString = Private_::HasToStringDefault_<T>::value;
121
122 /**
123 * \brief same as ToString()/1 - but without the potentially confusing multi-arg overloads (confused some template expansions).
124 */
125 template <typename T>
126 String UnoverloadedToString (const T& t);
127
128 /**
129 * define a 'std::formatter' for Stroika (and other types) for which formatter not already predefined - which use
130 * Characters::ToString (T)
131 */
132 template <IToString T>
134 qStroika_Foundation_Characters_FMT_PREFIX_::formatter<String, wchar_t> fDelegate2_;
135
136 template <class ParseContext>
137 constexpr typename ParseContext::iterator parse (ParseContext& ctx)
138 {
139 return fDelegate2_.parse (ctx);
140 }
141
142 template <class FmtContext>
143 typename FmtContext::iterator format (T s, FmtContext& ctx) const
144 {
145 return fDelegate2_.format (UnoverloadedToString (s), ctx);
146 }
147 };
148 template <IToString T>
149 struct ToStringFormatterASCII {
150 qStroika_Foundation_Characters_FMT_PREFIX_::formatter<String, char> fDelegate2_;
151
152 template <class ParseContext>
153 constexpr typename ParseContext::iterator parse (ParseContext& ctx)
154 {
155 return fDelegate2_.parse (ctx);
156 }
157
158 template <class FmtContext>
159 typename FmtContext::iterator format (T s, FmtContext& ctx) const
160 {
161 return fDelegate2_.format (UnoverloadedToString (s), ctx);
162 }
163 };
164
165}
166
167namespace Stroika::Foundation::Traversal {
168 template <typename T>
169 class Iterable;
170}
171
172namespace Stroika::Foundation::Characters::Private_ {
173
174 template <typename ELT>
175 void IsIterable_ (const Traversal::Iterable<ELT>*); // declared only: what it accepts is the question
176
177 /**
178 * \brief one of Stroika's containers, or a class derived from one: anything derived from a Traversal::Iterable
179 *
180 * \note Not with a lambda in the requires-expression: clang 15 to 18 then take the formatter partial specialization below as
181 * matching every type - "ambiguous partial specializations of 'formatter<int, wchar_t>'".
182 */
183 template <typename T>
184 concept IStroikaIterable_ = requires (const T* t) { IsIterable_ (t); };
185
186 /**
187 * \brief a type Stroika registers its ToString-based std::formatter for: Stroika's own types, and an application's with a
188 * ToString () method - never a standard library type
189 *
190 * The standard allows specializing its templates only for program-defined types ([namespace.std]), and formats its own types
191 * itself. Every other type Characters::ToString can print, Stroika's own formatting (_f, Format, DbgTrace) still formats - as
192 * its ToString (@see Characters::IFormattable).
193 */
194 template <typename T>
195 concept IUseToStringFormatterForFormatter_ = requires (const T& t) {
196 { t.ToString () } -> convertible_to<String>;
197 } or Common::IKeyValuePair<remove_cvref_t<T>> or Common::ICountedValue<remove_cvref_t<T>> or IStroikaIterable_<T>;
198
199}
200
201/**
202 * ToStringFormatter as the std::formatter (fmtlib's, where Stroika formats with fmtlib) of each IUseToStringFormatterForFormatter_
203 * type - so std::format itself formats them, as their Characters::ToString.
204 */
205template <Stroika::Foundation::Characters::Private_::IUseToStringFormatterForFormatter_ T>
206struct qStroika_Foundation_Characters_FMT_PREFIX_::formatter<T, wchar_t> : Stroika::Foundation::Characters::ToStringFormatter<T> {};
207template <Stroika::Foundation::Characters::Private_::IUseToStringFormatterForFormatter_ T>
208struct qStroika_Foundation_Characters_FMT_PREFIX_::formatter<T, char> : Stroika::Foundation::Characters::ToStringFormatterASCII<T> {};
209
210#if defined(__cpp_lib_format_ranges)
211namespace std {
212 /**
213 * Those that are ranges (Stroika's containers, Memory::BLOB ...) are formatted the same in every standard - as their ToString,
214 * not by the standard's range formatter (C++23), which would match them too.
215 */
216 template <ranges::input_range R>
218 constexpr range_format format_kind<R> = range_format::disabled;
219}
220#endif
221#if qStroika_HasComponent_fmtlib and FMT_VERSION >= 110000
222/**
223 * Where Stroika formats with fmtlib (no usable <format>), fmtlib formats no range itself - as before fmtlib 11, whose fmt/xchar.h
224 * includes fmt/ranges.h. Its range formatter would match Stroika's ranges too, and takes any range with a mapped_type for a map,
225 * needing elements that are tuples. A range is formatted as its Characters::ToString, as everywhere without range formatting (C++20).
226 * Strings aside: fmtlib's own headers have used this for theirs already, and it is disabled for a string anyway.
227 */
228template <typename R, typename CHAR_T>
229 requires (not std::convertible_to<R, fmt::basic_string_view<CHAR_T>>)
230struct fmt::range_format_kind<R, CHAR_T, void> : std::integral_constant<fmt::range_format, fmt::range_format::disabled> {};
231#endif
232
233/*
234 ********************************************************************************
235 ***************************** Implementation Details ***************************
236 ********************************************************************************
237 */
238
239#include "ToString.inl"
240
241#endif /*_Stroika_Foundation_Characters_ToString_h_*/
Check if legal to call Characters::ToString(T)...
Definition ToString.h:120
one of Stroika's containers, or a class derived from one: anything derived from a Traversal::Iterable
Definition ToString.h:184
a type Stroika registers its ToString-based std::formatter for: Stroika's own types,...
Definition ToString.h:195
String UnoverloadedToString(const T &t)
same as ToString()/1 - but without the potentially confusing multi-arg overloads (confused some templ...
Definition ToString.inl:435
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:424
STL namespace.