Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Characters/Format.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_Format_h_
5#define _Stroika_Foundation_Characters_Format_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <array>
10
13#include "Stroika/Foundation/Common/Common.h"
14#include "Stroika/Foundation/Common/Concepts.h"
16
17/**
18 * \file
19 *
20 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
21 */
22
24
25 /**
26 * \brief what types Stroika's formatting - FormatString (_f), Format, DbgTrace - formats: anything std::format can, and anything
27 * else Characters::ToString can print, as its ToString
28 *
29 * So it takes more types than std::format. It also formats a std::optional, an exception_ptr,
30 * an enum - as its Characters::ToString, a format-spec applying to that as to a String. (Stroika's own types, and any of
31 * yours with a ToString () method, std::format formats too: Stroika registers a std::formatter for them.)
32 */
33 template <typename T>
34 concept IFormattable = Common::StdCompat::formattable<remove_cvref_t<T>, wchar_t> or IToString<remove_cvref_t<T>>;
35
36 namespace Private_ {
37 /**
38 * f (the wformat_args of args): each argument itself where std::format can format it, else its Characters::ToString - kept
39 * alive through the call. Declared again in Debug/Trace.h, which cannot include this header.
40 */
41 template <typename RESULT, typename F, typename... ARGS>
42 RESULT WithFormatArgs_ (F&& f, const ARGS&... args);
43
44#if qStroika_Foundation_Debug_AssertionsChecked
45 /**
46 * Does s last as long as the program - true too where Stroika cannot tell (@see Execution::IsAddressReadOnlyAppLifetime).
47 * Defined in Format.cpp, so this header need not include Execution/Module.h.
48 */
49 bool LastsAppLifetime_ (wstring_view s);
50#endif
51
52 /**
53 * A string literal as a template argument - so "..."_f can widen it, and check it against its arguments, at compile time.
54 */
55 template <typename CHAR_T, size_t N>
56 struct FixedString_ {
57 using CharType = CHAR_T;
58 static constexpr size_t kChars = N - 1; // without the terminating NUL
59
60 CHAR_T fChars[N]{}; // as the literal, with its terminating NUL
61
62 consteval FixedString_ (const CHAR_T (&s)[N]);
63 };
64
65 // LITERAL, widened (for FormatLiteral)
66 template <FixedString_ LITERAL>
67 consteval array<wchar_t, decltype (LITERAL)::kChars + 1> Widen_ ();
68
69 // is LITERAL ASCII (as a narrow "..."_f must be)
70 template <FixedString_ LITERAL>
71 consteval bool IsASCII_ ();
72 }
73
74 /**
75 * \brief Stroika's format string: it always formats WIDE - each argument as std::formatter<T, wchar_t> formats it - into a
76 * String; written narrow ("..."_f) only as a terser spelling, so then it must be ASCII
77 *
78 * A narrow format string (FormatString<char>) must be ASCII - so what it means cannot depend on the compiler's narrow
79 * character set. A wide one may be any UNICODE, and so may what is formatted into either - a String, say.
80 *
81 * But EITHER way it produces a fully UNICODE String, and all the conversions - {} - are UNICODE.
82 *
83 * For narrow output instead - a std::string, in the program's narrow character set - use std::format with a narrow format
84 * string. Stroika's own types format there too (a String as AsNarrowSDKString (eIgnoreErrors) gives it), but not what only
85 * _f adds: the types std::format cannot format, formatted through their Characters::ToString (@see IFormattable).
86 *
87 * \note A FormatString<wchar_t> views its text, so that text must last as long as the program (@see String::FromStringConstant)
88 * - Debug builds check it. A FormatString<char> keeps its own, widened, copy; "..."_f's is static (@see FormatLiteral).
89 *
90 * \note What it formats: @see IFormattable.
91 *
92 * \par Example Usage
93 * \code
94 * String a = FormatString<char>{"Internet Media Type {} not supported"sv}(mediaType); // checked at run time
95 * String sameAsA = "Internet Media Type {} not supported"_f (mediaType); // checked at compile time
96 * DbgTrace ("prettyVersionString={}"_f, ppv);
97 * DbgTrace ("currentException={}"_f, current_exception ());
98 * DbgTrace ("seq={}"_f, Sequence<IO::Networking::URI>{...});
99 * \endcode
100 *
101 */
102 template <typename CHAR_T>
104 // a static_assert rather than a constraint, so Debug/Trace.h can forward-declare FormatString without including
105 // Common/Concepts.h - a redeclaration must repeat a constraint
107
108 public:
109 /**
110 */
111 FormatString () = delete;
112 FormatString (const FormatString&) = default;
113 constexpr FormatString (const basic_string_view<CHAR_T>& s);
114
115 public:
116 /**
117 * This retrieves the underlying string data - for the format string itself (regardless of how created, as a wstring_view).
118 */
119 constexpr wstring_view get () const;
120
121 public:
122 /**
123 * \brief the format string as the formatting library's own wstring_view (std's, or fmtlib's) - only for calling
124 * Common::StdCompat::vformat directly
125 */
126 constexpr qStroika_Foundation_Characters_FMT_PREFIX_::wstring_view getx_ () const;
127
128 public:
129 /**
130 * \brief format args - checked against the format string at run time: a mismatch throws format_error (a FormatLiteral,
131 * "..."_f, checks at compile time)
132 */
133 template <IFormattable... ARGS>
134 [[nodiscard]] inline String operator() (ARGS&&... args) const;
135 template <IFormattable... ARGS>
136 [[nodiscard]] inline String operator() (const locale& loc, ARGS&&... args) const; ///< \brief As above, with a locale
137
138 protected:
139 // not checked: for FormatString<char>, whose view is of its own copy, which lasts as long as it does, and FormatLiteral,
140 // whose view is of a static array
141 friend struct FormatString<char>;
142 struct Unchecked_ {};
143 constexpr FormatString (const basic_string_view<CHAR_T>& s, Unchecked_);
144
145 private:
146 wstring_view fSV_;
147 };
148 template <typename CHAR_T>
149 FormatString (const CHAR_T*) -> FormatString<CHAR_T>;
150 template <typename CHAR_T>
151 FormatString (const basic_string_view<CHAR_T>&) -> FormatString<CHAR_T>;
152
153 /**
154 * \brief a narrow FormatString: its text must be ASCII - checked at run time, it throws otherwise - and it keeps its own
155 * widened copy, so that text need not last
156 */
157 template <>
158 struct FormatString<char> {
159 private:
160 vector<wchar_t> fStringData_;
161 FormatString<wchar_t> fFmtStr_;
162
163 public:
164 FormatString () = delete;
165#if !qCompilerAndStdLib_vector_constexpr_Buggy
166 constexpr
167#endif
168 FormatString (const FormatString& src);
169#if !qCompilerAndStdLib_vector_constexpr_Buggy
170 constexpr
171#endif
172 FormatString (const basic_string_view<char>& s);
173
174#if !qCompilerAndStdLib_vector_constexpr_Buggy
175 constexpr
176#endif
177 FormatString& operator= (const FormatString& rhs);
178
179 constexpr wstring_view get () const;
180 constexpr qStroika_Foundation_Characters_FMT_PREFIX_::wstring_view getx_ () const;
181 template <Stroika_Foundation_Common_formattable_FilterOnStringLitOp_BWA (IFormattable)... ARGS>
182 [[nodiscard]] inline String operator() (ARGS&&... args) const;
183 template <Stroika_Foundation_Common_formattable_FilterOnStringLitOp_BWA (IFormattable)... ARGS>
184 [[nodiscard]] inline String operator() (const locale& loc, ARGS&&... args) const;
185 };
186
187 /**
188 * \brief what "..."_f is: a format string whose literal is part of its type - so it is widened (if narrow) at compile time,
189 * and checked against the arguments of each call at compile time, as std::format checks its format string
190 *
191 * It is a FormatString<wchar_t>, so anything taking a FormatString takes one - but only what takes a FormatLiteral checks
192 * the arguments: its operator (), Format, DbgTrace, TraceContextBumper, Logger::Log and WebServer::Response::write.
193 *
194 * \note Before Stroika v3.0d25, "..."_f was a FormatString<char> (or <wchar_t>): its format string checked only at run time
195 * (a mistake threw format_error), and a narrow one widened - into a newly allocated buffer - each time it ran.
196 *
197 * \par Example Usage
198 * \code
199 * String a = "provider={}"_f (provider);
200 * DbgTrace ("provider={}"_f, provider);
201 * // String b = "{} {}"_f (provider); // does not compile: two arguments wanted, one given
202 * \endcode
203 */
204 template <Private_::FixedString_ LITERAL>
205 struct FormatLiteral : FormatString<wchar_t> {
206 static_assert (Common::IAnyOf<typename decltype (LITERAL)::CharType, char, wchar_t>);
207 static_assert (Private_::IsASCII_<LITERAL> (), "a narrow \"...\"_f must be ASCII");
208
209 public:
210 /**
211 */
212 constexpr FormatLiteral ();
213
214 public:
215 /**
216 * As FormatString's operator (), but args are checked against the format string at compile time
217 */
218 template <Stroika_Foundation_Common_formattable_FilterOnStringLitOp_BWA (IFormattable)... ARGS>
219 [[nodiscard]] String operator() (ARGS&&... args) const;
220 template <Stroika_Foundation_Common_formattable_FilterOnStringLitOp_BWA (IFormattable)... ARGS>
221 [[nodiscard]] String operator() (const locale& loc, ARGS&&... args) const; ///< \brief As FormatString's, but checked at compile time
222
223 public:
224 /**
225 * \brief Unless the format string suits arguments of types ARGS..., a compile-time error - this is an immediate function
226 */
227 template <typename... ARGS>
228 static consteval void CheckArgs ();
229
230 public:
231 /**
232 * For code that names FormatString<char>, as a narrow "..."_f was before Stroika v3.0d25: this one copies the literal
233 */
234 operator FormatString<char> () const
235 requires (same_as<typename decltype (LITERAL)::CharType, char>);
236
237 public:
238 // what IFormatLiteral looks for - a mark, so Debug/Trace.h, which cannot include this header, can tell a FormatLiteral too
239 using FormatLiteralTag_ = void;
240
241 private:
242 // the literal, widened at compile time: static, so it lasts as long as the program
243 static constexpr array<wchar_t, decltype (LITERAL)::kChars + 1> kWide_ = Private_::Widen_<LITERAL> ();
244 };
245
246 /**
247 * \brief a FormatLiteral - what "..."_f is
248 *
249 * So a function taking a format string and its arguments can check them at compile time: give it an overload for an
250 * IFormatLiteral FORMAT_LITERAL, which calls FORMAT_LITERAL::CheckArgs<ARGS...> () (or formats with it, whose operator () checks).
251 */
252 template <typename T>
253 concept IFormatLiteral = requires { typename remove_cvref_t<T>::FormatLiteralTag_; };
254
255 /**
256 * \brief Create a format-string (see std::wformat_string or Stroika FormatString, or python 'f' strings) - a FormatLiteral
257 *
258 * The literal must be ASCII if narrow (a compile-time error otherwise) - narrow or wide, it formats wide, into a String
259 * (@see FormatString).
260 *
261 * \par Example Usage
262 * \code
263 * String a = "provider={}"_f (provider);
264 * DbgTrace ("provider={}"_f , provider);
265 * \endcode
266 */
267 inline namespace Literals {
268 template <Private_::FixedString_ LITERAL>
269 consteval FormatLiteral<LITERAL> operator""_f ();
270 }
271
272 /**
273 * \brief same as std::vformat, except that it always uses wformat_args, and produces a Stroika String
274 *
275 * \note FormatString typically created with _f, as in "foo={}"_f
276 */
277 template <typename CHAR_T>
278 [[nodiscard]] String VFormat (const FormatString<CHAR_T>& f, const Common::StdCompat::wformat_args& args);
279 template <typename CHAR_T>
280 [[nodiscard]] String VFormat (const locale& loc, const FormatString<CHAR_T>& f, const Common::StdCompat::wformat_args& args); ///< \brief As above, with a locale
281
282 /**
283 * \brief printf-style formatting of a va_list, into a String - with one difference from vswprintf: %s takes a const wchar_t*
284 * (as Microsoft's does - the better reading, with wide characters)
285 *
286 * New code formats with "..."_f; a C format string, with CString::Format.
287 */
288 String FormatV (const wchar_t* format, va_list argsList);
289 [[deprecated ("Since Stroika v3.0d6 - use _f format strings - not old style c format strings - use CString::Format for old style C "
290 "format strings")]] String
291 Format (const wchar_t* format, ...);
292
293 /**
294 * \brief Like std::format, but formats wide, into a String: f (args...) - its args checked at run time
295 */
296 template <typename CHAR_T, IFormattable... ARGS>
297 String Format (const FormatString<CHAR_T>& f, ARGS&&... args);
298 template <typename CHAR_T, IFormattable... ARGS>
299 String Format (const locale& l, const FormatString<CHAR_T>& f, ARGS&&... args); ///< \brief Like std::format, with a locale
300 template <Private_::FixedString_ LITERAL, Stroika_Foundation_Common_formattable_FilterOnStringLitOp_BWA (IFormattable)... ARGS>
301 String Format (const FormatLiteral<LITERAL>& f, ARGS&&... args); ///< \brief Like std::format - args checked against f at compile time
302 template <Private_::FixedString_ LITERAL, Stroika_Foundation_Common_formattable_FilterOnStringLitOp_BWA (IFormattable)... ARGS>
303 String Format (const locale& l, const FormatLiteral<LITERAL>& f, ARGS&&... args); ///< \brief Like std::format, with a locale - checked at compile time
304
305}
306
307/*
308 ********************************************************************************
309 ***************************** Implementation Details ***************************
310 ********************************************************************************
311 */
312#include "Format.inl"
313
314#endif /*_Stroika_Foundation_Characters_Format_h_*/
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
what types Stroika's formatting - FormatString (_f), Format, DbgTrace - formats: anything std::format...
Check if legal to call Characters::ToString(T)...
Definition ToString.h:120
Concept: trivial shorthand for variadic same_as A or same_as B, or ...
Definition Concepts.h:122
String FormatV(const wchar_t *format, va_list argsList)
printf-style formatting of a va_list, into a String - with one difference from vswprintf: s takes a c...
String VFormat(const FormatString< CHAR_T > &f, const Common::StdCompat::wformat_args &args)
same as std::vformat, except that it always uses wformat_args, and produces a Stroika String
what "..."_f is: a format string whose literal is part of its type - so it is widened (if narrow) at ...
static consteval void CheckArgs()
Unless the format string suits arguments of types ARGS..., a compile-time error - this is an immediat...
Stroika's format string: it always formats WIDE - each argument as std::formatter<T,...
String operator()(ARGS &&... args) const
format args - checked against the format string at run time: a mismatch throws format_error (a Format...
constexpr qStroika_Foundation_Characters_FMT_PREFIX_::wstring_view getx_() const
the format string as the formatting library's own wstring_view (std's, or fmtlib's) - only for callin...