Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
StringBuilder.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Characters_StringBuilder_h_
5#define _Stroika_Foundation_Characters_StringBuilder_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
12
13/**
14 * \file
15 *
16 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
17 *
18 * TODO:
19 * @todo Think about how to add support for STL manipulator/inserters like endl;
20 *
21 * @todo Consider adding (back) reserve/capacity methods, but be sure to document these are in units of
22 * BufferElementType not characters.
23 */
25 template <typename T>
26 String UnoverloadedToString (const T& t);
27}
28
30
31 namespace Private_ {
32 template <typename T>
33 concept IToString = requires (T t) {
34 { UnoverloadedToString (t) } -> convertible_to<Characters::String>;
35 };
36 }
37
38 /**
39 * \brief rarely used directly - defaults generally fine
40 *
41 * BUF_CHAR_T of char32_t probably does better if definitely using a lot of wide unicode characters.
42 * BUF_CHAR_T of char8_t probably best for mostly ASCII text. Note - GetAt/SetAt very slow
43 * unless using char32_t.
44 *
45 * Maybe easy to support all at once.
46 */
47 template <IUNICODECanAlwaysConvertTo BUF_CHAR_T = char8_t, size_t INLINE_BUF_SIZE = 128>
49 /**
50 * Note that kInlineBufferSize is measured in 'buffer elements' - not (necessarily the same as) bytes or Characters.
51 */
52 static constexpr size_t kInlineBufferSize = INLINE_BUF_SIZE;
53
54 /**
55 */
56 using BufferElementType = BUF_CHAR_T;
57 };
58
59 /**
60 * \brief Similar to String, but intended to more efficiently construct a String. Mutable type (String is largely immutable).
61 *
62 * Has operator String() co can be used by value most places you can use a String.
63 *
64 * @see String
65 * @see .Net StringBuilder - http://msdn.microsoft.com/en-us/library/system.text.stringbuilder(v=vs.110).aspx
66 * @see Java StringBuilder - http://docs.oracle.com/javase/7/docs/api/java/lang/StringBuilder.html
67 *
68 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
69 *
70 * \note pointless to ever create StringBuilder and not use, so [[nodiscard]] appropriate
71 */
72 template <typename OPTIONS = StringBuilder_Options<>>
73 class [[nodiscard]] StringBuilder {
74 public:
75 /**
76 */
77 using value_type = Character;
78
79 public:
80 static constexpr size_t kInlineBufferSize = OPTIONS::kInlineBufferSize;
81
82 public:
83 using BufferElementType = typename OPTIONS::BufferElementType;
85
86 public:
87 /**
88 */
89 StringBuilder () noexcept = default;
90 StringBuilder (const StringBuilder&) = default;
91 StringBuilder (const String& initialValue);
92 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
93 StringBuilder (span<const CHAR_T> initialValue);
94
95 public:
96 nonvirtual StringBuilder& operator= (const StringBuilder& rhs) = default;
97 template <convertible_to<String> T>
98 nonvirtual StringBuilder& operator= (const T& rhs);
99
100 public:
101 /**
102 * Append the given argument characters to this buffer.
103 *
104 * argument characters can be given by
105 * o span<unicode (or narrow ASCII) characters>
106 * o const T* - nul-terminated array of unicode (or narrow ASCII) characters
107 * o basic_string<unicode (or narrow ASCII) characters>
108 * o basic_string_view<unicode (or narrow ASCII) characters>
109 * o String
110 * o Character
111 *
112 * This function appends as IF the argument was converted to a UNICODE string, and then
113 * appended.
114 */
115 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
116 nonvirtual void Append (span<const CHAR_T> s);
117 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
118 nonvirtual void Append (span<CHAR_T> s);
119 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
120 nonvirtual void Append (const CHAR_T* s);
121 template <IStdBasicStringCompatibleCharacter CHAR_T>
122 nonvirtual void Append (const basic_string<CHAR_T>& s)
124 template <IStdBasicStringCompatibleCharacter CHAR_T>
125 nonvirtual void Append (const basic_string_view<CHAR_T>& s)
127 nonvirtual void Append (const String& s);
128 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
129 nonvirtual void Append (CHAR_T c);
130
131 // clang-format off
132 public:
133 /**
134 * @aliases Append
135 */
136 template <typename APPEND_ARG_T>
137 nonvirtual auto operator+= (APPEND_ARG_T&& a)
138 -> StringBuilder& requires (requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); })
139 #if qCompilerAndStdLib_template_Requires_templateDeclarationMatchesOutOfLine_Buggy
140 {
141 if constexpr (requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); }) {
142 Append (forward<APPEND_ARG_T> (a));
143 }
144 else {
145 Append (Characters::UnoverloadedToString (forward<APPEND_ARG_T> (a)));
146 }
147 return *this;
148 }
149 #else
150 ;
151 #endif
152
153#if qCompiler_IUseToStringFormatterForFormatter_Buggy
154 public:
155 // this hack has nothing todo with real IUseToStringFormatterForFormatter bug - but is needed as artifact of workaround
156 Characters::String ToString () const
157 {
158 return this->str ();
159 }
160#endif
161
162 public:
163 /**
164 * @aliases Append if that would work, and otherwise alias for Append (ToString(arg)), if that would work;
165 */
166 template <typename APPEND_ARG_T>
167 nonvirtual auto
168 operator<< (APPEND_ARG_T&& a)
169 -> StringBuilder& requires (Characters::Private_::IToString<APPEND_ARG_T> or requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); })
170#if qCompilerAndStdLib_template_Requires_templateDeclarationMatchesOutOfLine_Buggy
171 {
172 if constexpr (requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); }) {
173 Append (forward<APPEND_ARG_T> (a));
174 }
175 else {
176 Append (Characters::UnoverloadedToString (forward<APPEND_ARG_T> (a)));
177 }
178 return *this;
179 }
180#else
181 ;
182#endif
183 // clang-format on
184
185 public :
186 /**
187 */
188 nonvirtual void push_back (Character c);
189
190 public:
191 /**
192 * returns number of characters (not bytes, not including any possible NUL-terminator)
193 */
194 nonvirtual size_t size () const noexcept;
195
196 public:
197 /**
198 * Returns true if this is an empty string (aka iff size () == 0);
199 */
200 nonvirtual bool empty () const noexcept;
201
202 public:
203 /**
204 * \note - this can be very slow if OPTIONS::BufferElementType != char32_t (but if StringBuilder_Options<char32_t> its fast)
205 */
206 nonvirtual Character GetAt (size_t index) const noexcept;
207
208 public:
209 /**
210 * \note - this can be very slow if OPTIONS::BufferElementType != char32_t (but if StringBuilder_Options<char32_t> its fast)
211 */
212 nonvirtual void SetAt (Character item, size_t index) noexcept;
213
214 public:
215 /**
216 * \brief return (read-only) Character object
217 *
218 * @aliases GetAt (size_t i) const;
219 *
220 * \note this is very inefficient, unless using StringBuilder_Options<char32_t> - in which case its quite fast
221 */
222 nonvirtual const Character operator[] (size_t i) const noexcept;
223
224 public:
225 /**
226 * Mimic the String::InsertAt API, except modify in place.
227 */
228 template <Common::IAnyOf<char, Character, String, span<const Character>, span<Character>> T>
229 nonvirtual void InsertAt (T c, size_t at);
230
231 public:
232 /**
233 * Change the size of this object to sz = where sz must be <= size()
234 */
235 nonvirtual void ShrinkTo (size_t sz) noexcept;
236
237 public:
238 /**
239 * Only specifically specialized variants are supported. Convert String losslessly into a
240 * o String or
241 * o wstring
242 * o u8string
243 * o u16string
244 * o u32string
245 */
246 template <Common::IAnyOf<String, wstring, u8string, u16string, u32string> RESULT_T>
247 nonvirtual RESULT_T As () const;
248
249 public:
250 /*
251 * (mostly) explicit operator T () provides an alternative syntax to As<> - depending on user
252 * preference or context. Note - its important that this is explicit - to avoid
253 * creating overload problems.
254 */
255 nonvirtual /*explicit*/ operator String () const;
256 nonvirtual explicit operator wstring () const;
257 nonvirtual explicit operator u8string () const;
258 nonvirtual explicit operator u16string () const;
259 nonvirtual explicit operator u32string () const;
260
261 nonvirtual explicit operator span<const BufferElementType> () const;
262
263 public:
264 /**
265 */
266 nonvirtual void clear () noexcept;
267
268 public:
269 /**
270 * mimic wstringstream method
271 */
272 nonvirtual String str () const;
273
274 public:
275 /**
276 * \brief number of characters, not bytes or code-points
277 *
278 * @aliases size ()
279 */
280 nonvirtual size_t length () const noexcept;
281
282 public:
283 /**
284 * ONLY valid til the next non-const call to StringBuilder.
285 * See also GetData (to select a different charType).
286 */
287 nonvirtual span<BufferElementType> data ();
288 nonvirtual span<const BufferElementType> data () const;
289
290 public:
291 /**
292 * \brief access a span of data located inside the StringBuilder. Return internal pointer, or pointer internal to possiblyUsedBuffer
293 *
294 * \note Lifetime of resulting span is ONLY until the next change to the StackBuffer OR the StringBuilder.
295 * \note The pointer MIGHT refer to data inside the (possibly resized) StackBuffer, or be internal to the StringBuilder
296 *
297 * The point of this queer API is too allow accessing the internal data by pointer, but allow StringBuilder to change
298 * its internal representation (not necessarily matching the kind of string being requested).
299 *
300 * \note Caller should ignore the size of possiblyUsedBuffer; its for internal use inside of GetData() - and may not match the size
301 * of the resulting string/span. Note also, the span will not in general be NUL-terminated.
302 *
303 * \note Why use this function?
304 * You would think the point of StringBuilder was to - well - build a string - right? So why not use the str() API.
305 * Well, that allocates memory, which must be freed, and that is not cost free. For some short-lived strings, it CAN
306 * be cheaper to just peek at the constructed in memory stack based String already being produced in this StringBuilder.
307 *
308 * But this needs to be done in a way with data hiding (so we can change the internal representation of the StringBuilder class as needed)
309 * and with respect for the possibility that the string could be large (so break out of any small-string optimizations).
310 *
311 * Passing in a reference to the 'StackBuffer' class is a compromise among all these considerations. The only cost
312 * is initializing a pointer, and checking that pointer on destruction, if no memory allocation is needed.
313 *
314 * \par Example Usage:
315 * \code
316 * Memory::StackBuffer<wchar_t> probablyIgnoredBuf;
317 * span<const wchar_t> s = sb.GetData (&probablyIgnoredBuf);
318 * \endcode
319 *
320 * \code
321 * Memory::StackBuffer<wchar_t> probablyIgnoredBuf;
322 * out.Write (sb.GetData (&probablyIgnoredBuf));
323 * \endcode
324 */
326 nonvirtual span<const CHAR_T> GetData (Memory::StackBuffer<CHAR_T>* probablyIgnoredBuf) const
327 requires (not is_const_v<CHAR_T>);
328
329 public:
330 // @todo cleanup
331 bool operator== (const String& rhs) const;
332 bool operator== (const StringBuilder& rhs) const;
333
334 public:
335 // ape std::string API
336 // @todo cleanup
337 void erase (size_t from);
338 void erase (size_t from, size_t count);
339
340 public:
341 [[deprecated ("Since Stroika v3.0d1, use span{} argument")]] StringBuilder (const wchar_t* start, const wchar_t* end)
342 {
343 Append (span{start, end});
344 }
345 [[deprecated ("Since Stroika v3.0d1, use span{} argument")]] void Append (const char16_t* s, const char16_t* e)
346 {
347 Append (span{s, e});
348 }
349 [[deprecated ("Since Stroika v3.0d1, use span{} argument")]] void Append (const char32_t* s, const char32_t* e)
350 {
351 Append (span{s, e});
352 }
353 [[deprecated ("Since Stroika v3.0d1, use span{} argument")]] void Append (const wchar_t* s, const wchar_t* e)
354 {
355 Append (span{s, e});
356 }
357 [[deprecated ("Since Stroika v3.0d1, use span{} argument")]] void Append (const Character* s, const Character* e)
358 {
359 Append (span{s, e});
360 }
361 template <typename RESULT_T>
362 [[deprecated ("Since Stroika v3.0d2 - use As/0")]] void As (RESULT_T* into) const
363 requires (same_as<RESULT_T, String> or same_as<RESULT_T, wstring>)
364 {
365 RequireNotNull (into);
366 if constexpr (same_as<RESULT_T, String>) {
367 *into = str ();
368 }
369 if constexpr (same_as<RESULT_T, wstring>) {
370 *into = str ().template As<wstring> ();
371 }
372 }
373
374 private:
375 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fAssertExternallySyncrhonized_;
376
377 private:
378 Memory::InlineBuffer<BufferElementType, kInlineBufferSize> fData_{}; // not nul-terminated
379 };
380
381}
382
383/*
384 ********************************************************************************
385 ***************************** Implementation Details ***************************
386 ********************************************************************************
387 */
388#include "StringBuilder.inl"
389
390#endif /*_Stroika_Foundation_Characters_StringBuilder_h_*/
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
nonvirtual void Append(span< const CHAR_T > s)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
UNICODE string can be always be converted into array of this type.
Definition Character.h:132
IUNICODECanUnambiguouslyConvertFrom is any 'character representation type' where array of them unambi...
Definition Character.h:179
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
rarely used directly - defaults generally fine