Stroika Library 3.0d16
 
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 template <typename OPTIONS = StringBuilder_Options<>>
71 class [[nodiscard]] StringBuilder {
72 public:
73 /**
74 */
75 using value_type = Character;
76
77 public:
78 static constexpr size_t kInlineBufferSize = OPTIONS::kInlineBufferSize;
79
80 public:
81 using BufferElementType = typename OPTIONS::BufferElementType;
83
84 public:
85 /**
86 */
87 StringBuilder () noexcept = default;
88 StringBuilder (const StringBuilder&) = default;
89 StringBuilder (const String& initialValue);
90 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
91 StringBuilder (span<const CHAR_T> initialValue);
92
93 public:
94 nonvirtual StringBuilder& operator= (const StringBuilder& rhs) = default;
95 template <convertible_to<String> T>
96 nonvirtual StringBuilder& operator= (const T& rhs);
97
98 public:
99 /**
100 * Append the given argument characters to this buffer.
101 *
102 * argument characters can be given by
103 * o span<unicode (or narrow ASCII) characters>
104 * o const T* - nul-terminated array of unicode (or narrow ASCII) characters
105 * o basic_string<unicode (or narrow ASCII) characters>
106 * o basic_string_view<unicode (or narrow ASCII) characters>
107 * o String
108 * o Character
109 *
110 * This function appends as IF the argument was converted to a UNICODE string, and then
111 * appended.
112 */
113 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
114 nonvirtual void Append (span<const CHAR_T> s);
115 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
116 nonvirtual void Append (span<CHAR_T> s);
117 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
118 nonvirtual void Append (const CHAR_T* s);
119 template <IStdBasicStringCompatibleCharacter CHAR_T>
120 nonvirtual void Append (const basic_string<CHAR_T>& s)
122 template <IStdBasicStringCompatibleCharacter CHAR_T>
123 nonvirtual void Append (const basic_string_view<CHAR_T>& s)
125 nonvirtual void Append (const String& s);
126 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
127 nonvirtual void Append (CHAR_T c);
128
129 // clang-format off
130 public:
131 /**
132 * @aliases Append
133 */
134 template <typename APPEND_ARG_T>
135 nonvirtual auto operator+= (APPEND_ARG_T&& a)
136 -> StringBuilder& requires (requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); })
137 #if qCompilerAndStdLib_template_Requires_templateDeclarationMatchesOutOfLine_Buggy
138 {
139 if constexpr (requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); }) {
140 Append (forward<APPEND_ARG_T> (a));
141 }
142 else {
143 Append (Characters::UnoverloadedToString (forward<APPEND_ARG_T> (a)));
144 }
145 return *this;
146 }
147 #else
148 ;
149 #endif
150
151#if qCompiler_IUseToStringFormatterForFormatter_Buggy
152 public:
153 // this hack has nothing todo with real IUseToStringFormatterForFormatter bug - but is needed as artifact of workaround
154 Characters::String ToString () const
155 {
156 return this->str ();
157 }
158#endif
159
160 public:
161 /**
162 * @aliases Append if that would work, and otherwise alias for Append (ToString(arg)), if that would work;
163 */
164 template <typename APPEND_ARG_T>
165 nonvirtual auto
166 operator<< (APPEND_ARG_T&& a)
167 -> StringBuilder& requires (Characters::Private_::IToString<APPEND_ARG_T> or requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); })
168#if qCompilerAndStdLib_template_Requires_templateDeclarationMatchesOutOfLine_Buggy
169 {
170 if constexpr (requires (StringBuilder& s, APPEND_ARG_T&& a) { s.Append (forward<APPEND_ARG_T> (a)); }) {
171 Append (forward<APPEND_ARG_T> (a));
172 }
173 else {
174 Append (Characters::UnoverloadedToString (forward<APPEND_ARG_T> (a)));
175 }
176 return *this;
177 }
178#else
179 ;
180#endif
181 // clang-format on
182
183 public :
184 /**
185 */
186 nonvirtual void push_back (Character c);
187
188 public:
189 /**
190 * returns number of characters (not bytes, not including any possible NUL-terminator)
191 */
192 nonvirtual size_t size () const noexcept;
193
194 public:
195 /**
196 * Returns true if this is an empty string (aka iff size () == 0);
197 */
198 nonvirtual bool empty () const noexcept;
199
200 public:
201 /**
202 * \note - this can be very slow if OPTIONS::BufferElementType != char32_t (but if StringBuilder_Options<char32_t> its fast)
203 */
204 nonvirtual Character GetAt (size_t index) const noexcept;
205
206 public:
207 /**
208 * \note - this can be very slow if OPTIONS::BufferElementType != char32_t (but if StringBuilder_Options<char32_t> its fast)
209 */
210 nonvirtual void SetAt (Character item, size_t index) noexcept;
211
212 public:
213 /**
214 * \brief return (read-only) Character object
215 *
216 * @aliases GetAt (size_t i) const;
217 *
218 * \note this is very inefficient, unless using StringBuilder_Options<char32_t> - in which case its quite fast
219 */
220 nonvirtual const Character operator[] (size_t i) const noexcept;
221
222 public:
223 /**
224 * Mimic the String::InsertAt API, except modify in place.
225 */
226 template <Common::IAnyOf<char, Character, String, span<const Character>, span<Character>> T>
227 nonvirtual void InsertAt (T c, size_t at);
228
229 public:
230 /**
231 * Change the size of this object to sz = where sz must be <= size()
232 */
233 nonvirtual void ShrinkTo (size_t sz) noexcept;
234
235 public:
236 /**
237 * Only specifically specialized variants are supported. Convert String losslessly into a
238 * o String or
239 * o wstring
240 * o u8string
241 * o u16string
242 * o u32string
243 */
244 template <Common::IAnyOf<String, wstring, u8string, u16string, u32string> RESULT_T>
245 nonvirtual RESULT_T As () const;
246
247 public:
248 /*
249 * (mostly) explicit operator T () provides an alternative syntax to As<> - depending on user
250 * preference or context. Note - its important that this is explicit - to avoid
251 * creating overload problems.
252 */
253 nonvirtual /*explicit*/ operator String () const;
254 nonvirtual explicit operator wstring () const;
255 nonvirtual explicit operator u8string () const;
256 nonvirtual explicit operator u16string () const;
257 nonvirtual explicit operator u32string () const;
258
259 nonvirtual explicit operator span<const BufferElementType> () const;
260
261 public:
262 /**
263 */
264 nonvirtual void clear () noexcept;
265
266 public:
267 /**
268 * mimic wstringstream method
269 */
270 nonvirtual String str () const;
271
272 public:
273 /**
274 * \brief number of characters, not bytes or code-points
275 *
276 * @aliases size ()
277 */
278 nonvirtual size_t length () const noexcept;
279
280 public:
281 /**
282 * ONLY valid til the next non-const call to StringBuilder.
283 * See also GetData (to select a different charType).
284 */
285 nonvirtual span<BufferElementType> data ();
286 nonvirtual span<const BufferElementType> data () const;
287
288 public:
289 /**
290 * \brief access a span of data located inside the StringBuilder. Return internal pointer, or pointer internal to possiblyUsedBuffer
291 *
292 * \note Lifetime of resulting span is ONLY until the next change to the StackBuffer OR the StringBuilder.
293 * \note The pointer MIGHT refer to data inside the (possibly resized) StackBuffer, or be internal to the StringBuilder
294 *
295 * The point of this queer API is too allow accessing the internal data by pointer, but allow StringBuilder to change
296 * its internal representation (not necessarily matching the kind of string being requested).
297 *
298 * \note Caller should ignore the size of possiblyUsedBuffer; its for internal use inside of GetData() - and may not match the size
299 * of the resulting string/span. Note also, the span will not in general be NUL-terminated.
300 *
301 * \note Why use this function?
302 * You would think the point of StringBuilder was to - well - build a string - right? So why not use the str() API.
303 * Well, that allocates memory, which must be freed, and that is not cost free. For some short-lived strings, it CAN
304 * be cheaper to just peek at the constructed in memory stack based String already being produced in this StringBuilder.
305 *
306 * 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)
307 * and with respect for the possibility that the string could be large (so break out of any small-string optimizations).
308 *
309 * Passing in a reference to the 'StackBuffer' class is a compromise among all these considerations. The only cost
310 * is initializing a pointer, and checking that pointer on destruction, if no memory allocation is needed.
311 *
312 * \par Example Usage:
313 * \code
314 * Memory::StackBuffer<wchar_t> probablyIgnoredBuf;
315 * span<const wchar_t> s = sb.GetData (&probablyIgnoredBuf);
316 * \endcode
317 *
318 * \code
319 * Memory::StackBuffer<wchar_t> probablyIgnoredBuf;
320 * out.Write (sb.GetData (&probablyIgnoredBuf));
321 * \endcode
322 */
324 nonvirtual span<const CHAR_T> GetData (Memory::StackBuffer<CHAR_T>* probablyIgnoredBuf) const
325 requires (not is_const_v<CHAR_T>);
326
327 public:
328 // @todo cleanup
329 bool operator== (const String& rhs) const;
330 bool operator== (const StringBuilder& rhs) const;
331
332 public:
333 // ape std::string API
334 // @todo cleanup
335 void erase (size_t from);
336 void erase (size_t from, size_t count);
337
338 public:
339 [[deprecated ("Since Stroika v3.0d1, use span{} argument")]] StringBuilder (const wchar_t* start, const wchar_t* end)
340 {
341 Append (span{start, end});
342 }
343 [[deprecated ("Since Stroika v3.0d1, use span{} argument")]] void Append (const char16_t* s, const char16_t* e)
344 {
345 Append (span{s, e});
346 }
347 [[deprecated ("Since Stroika v3.0d1, use span{} argument")]] void Append (const char32_t* s, const char32_t* e)
348 {
349 Append (span{s, e});
350 }
351 [[deprecated ("Since Stroika v3.0d1, use span{} argument")]] void Append (const wchar_t* s, const wchar_t* e)
352 {
353 Append (span{s, e});
354 }
355 [[deprecated ("Since Stroika v3.0d1, use span{} argument")]] void Append (const Character* s, const Character* e)
356 {
357 Append (span{s, e});
358 }
359 template <typename RESULT_T>
360 [[deprecated ("Since Stroika v3.0d2 - use As/0")]] void As (RESULT_T* into) const
361 requires (same_as<RESULT_T, String> or same_as<RESULT_T, wstring>)
362 {
363 RequireNotNull (into);
364 if constexpr (same_as<RESULT_T, String>) {
365 *into = str ();
366 }
367 if constexpr (same_as<RESULT_T, wstring>) {
368 *into = str ().template As<wstring> ();
369 }
370 }
371
372 private:
373 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fAssertExternallySyncrhonized_;
374
375 private:
376 Memory::InlineBuffer<BufferElementType, kInlineBufferSize> fData_{}; // not nul-terminated
377 };
378
379}
380
381/*
382 ********************************************************************************
383 ***************************** Implementation Details ***************************
384 ********************************************************************************
385 */
386#include "StringBuilder.inl"
387
388#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