FINAL CUT
fstring.h
1 /***********************************************************************
2 * fstring.h - Unicode string class with UTF-8 support *
3 * *
4 * This file is part of the FINAL CUT widget toolkit *
5 * *
6 * Copyright 2012-2026 Markus Gans *
7 * *
8 * FINAL CUT is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as *
10 * published by the Free Software Foundation; either version 3 of *
11 * the License, or (at your option) any later version. *
12 * *
13 * FINAL CUT is distributed in the hope that it will be useful, but *
14 * WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public *
19 * License along with this program. If not, see *
20 * <http://www.gnu.org/licenses/>. *
21 ***********************************************************************/
22 
23 /* Standalone class
24  * ════════════════
25  *
26  * ▕▔▔▔▔▔▔▔▔▔▏
27  * ▕ FString ▏
28  * ▕▁▁▁▁▁▁▁▁▁▏
29  */
30 
31 #ifndef FSTRING_H
32 #define FSTRING_H
33 
34 #if !defined (USE_FINAL_H) && !defined (COMPILE_FINAL_CUT)
35  #error "Only <final/final.h> can be included directly."
36 #endif
37 
38 #include <langinfo.h>
39 
40 #include <cassert>
41 #include <cerrno> // for read errno
42 #include <cfloat>
43 #include <climits>
44 #include <cmath>
45 #include <cstdarg> // need for va_list, va_start and va_end
46 #include <cstdint>
47 #include <cstdio> // need for vsprintf
48 #include <cstring>
49 #include <cwchar>
50 #include <cwctype>
51 
52 #include <algorithm>
53 #include <array>
54 #include <iostream>
55 #include <limits>
56 #include <memory>
57 #include <mutex>
58 #include <new>
59 #include <stdexcept>
60 #include <string>
61 #include <type_traits>
62 #include <utility>
63 #include <vector>
64 
65 #include "final/fc.h"
66 #include "final/ftypes.h"
67 
68 namespace finalcut
69 {
70 
71 // Class forward declaration
72 class FString;
73 
74 // Global using-declaration
75 using FStringList = std::vector<FString>;
76 
77 
78 //----------------------------------------------------------------------
79 // class FString
80 //----------------------------------------------------------------------
81 
82 class FString
83 {
84  public:
85  // Exception handling
86  class formatting_error : public std::runtime_error
87  {
88  public:
89  using std::runtime_error::runtime_error;
90  formatting_error (const formatting_error&) = default;
91  ~formatting_error() noexcept override;
92  };
93 
94  // Using-declarations - type aliases
95  using size_type = std::wstring::size_type;
96  using difference_type = std::wstring::difference_type;
97  using iterator = std::wstring::iterator;
98  using const_iterator = std::wstring::const_iterator;
99  using reverse_iterator = std::wstring::reverse_iterator;
100  using const_reverse_iterator = std::wstring::const_reverse_iterator;
101  using reference = std::wstring::reference;
102  using const_reference = std::wstring::const_reference;
103  using value_type = wchar_t;
104 
105  // Constant
106  static constexpr size_type npos = std::wstring::npos;
107 
108  // Constructors
109  FString () = default;
110  explicit FString (int);
111  explicit FString (size_type);
112  FString (size_type, wchar_t);
113  FString (size_type, const UniChar&);
114  FString (const FString&); // copy constructor
115  FString (FString&&) noexcept; // move constructor
116  FString (const std::wstring&); // implicit conversion constructor
117 #if __cplusplus >= 201703L
118  FString (const std::wstring_view&); // implicit conversion constructor
119 #endif
120  FString (std::wstring&&); // implicit conversion constructor
121  FString (const wchar_t[]); // implicit conversion constructor
122  FString (const std::string&); // implicit conversion constructor
123 #if __cplusplus >= 201703L
124  FString (const std::string_view&); // implicit conversion constructor
125 #endif
126  FString (const char[]); // implicit conversion constructor
127  FString (const UniChar&); // implicit conversion constructor
128  FString (const wchar_t); // implicit conversion constructor
129  FString (const char); // implicit conversion constructor
130 
131  // Destructor
132  virtual ~FString () noexcept;
133 
134  // Copy assignment operator (=)
135  auto operator = (const FString&) -> FString&;
136 
137  // Move assignment operator (=)
138  auto operator = (FString&&) noexcept -> FString&;
139 
140  // Compound assignment operators
141  auto operator += (const FString&) -> const FString&;
142 
143  // Stream insertion operators
144  auto operator << (const FString&) -> FString&;
145  auto operator << (const UniChar&) -> FString&;
146  auto operator << (const wchar_t) -> FString&;
147  auto operator << (const char) -> FString&;
148  template <typename NumT
149  , std::enable_if_t< ( std::is_integral<NumT>::value
150  && ! std::is_same<NumT, bool>::value
151  && ! std::is_pointer<NumT>::value )
152  || ( std::is_floating_point<NumT>::value
153  && ! std::is_pointer<NumT>::value )
154  , int> = 0 >
155  auto operator << (NumT) -> FString&;
156 
157  // Stream extraction operator friends
158  friend inline auto operator >> (const FString& lhs, FString& rhs) -> const FString&
159  {
160  rhs.string.append(lhs.toWString());
161  return lhs;
162  }
163 
164  friend inline auto operator >> (const FString& lhs, std::wstring& rhs) -> const FString&
165  {
166  rhs.append(lhs.toWString());
167  return lhs;
168  }
169 
170  friend inline auto operator >> (const FString& lhs, std::string& rhs) -> const FString&
171  {
172  rhs.append(lhs.toString());
173  return lhs;
174  }
175 
176  friend inline auto operator >> (const FString& lhs, wchar_t& rhs) -> const FString&
177  {
178  rhs = ( ! lhs.isEmpty() ) ? lhs.string[0] : L'\0';
179  return lhs;
180  }
181 
182  friend inline auto operator >> (const FString& lhs, char& rhs) -> const FString&
183  {
184  rhs = ( ! lhs.isEmpty() ) ? char(uChar(lhs.string[0])) : '\0';
185  return lhs;
186  }
187 
188  friend inline auto operator >> (const FString& lhs, sInt16& rhs) -> const FString&
189  {
190  rhs = lhs.toShort();
191  return lhs;
192  }
193 
194  friend inline auto operator >> (const FString& lhs, uInt16& rhs) -> const FString&
195  {
196  rhs = lhs.toUShort();
197  return lhs;
198  }
199 
200  friend inline auto operator >> (const FString& lhs, sInt32& rhs) -> const FString&
201  {
202  rhs = lhs.toInt();
203  return lhs;
204  }
205 
206  friend inline auto operator >> (const FString& lhs, uInt32& rhs) -> const FString&
207  {
208  rhs = lhs.toUInt();
209  return lhs;
210  }
211 
212  friend inline auto operator >> (const FString& lhs, sInt64& rhs) -> const FString&
213  {
214  rhs = lhs.toLong();
215  return lhs;
216  }
217 
218  friend inline auto operator >> (const FString& lhs, uInt64& rhs) -> const FString&
219  {
220  rhs = lhs.toULong();
221  return lhs;
222  }
223 
224  friend inline auto operator >> (const FString& lhs, double& rhs) -> const FString&
225  {
226  rhs = lhs.toDouble();
227  return lhs;
228  }
229 
230  friend inline auto operator >> (const FString& lhs, float& rhs) -> const FString&
231  {
232  rhs = lhs.toFloat();
233  return lhs;
234  }
235 
236  // Index operators
237  template <typename IndexT>
238  constexpr auto operator [] (const IndexT) -> reference;
239  template <typename IndexT>
240  constexpr auto operator [] (const IndexT) const -> const_reference;
241 
242  // Function call operators
243  explicit operator bool () const noexcept;
244  auto operator () () const noexcept -> const FString&;
245 
246  // Comparison operator friends
247  friend inline auto operator < (const FString& lhs, const FString& rhs) -> bool
248  {
249  return lhs.string < rhs.string;
250  }
251 
252  template <typename CharT
253  , enable_if_char_ptr_t<CharT> = nullptr>
254  friend inline auto operator < (const FString& lhs, const CharT& rhs) -> bool
255  {
256  std::lock_guard<std::mutex> lock(lhs.char_string_mutex);
257  lhs.char_string = lhs.internal_toCharString(lhs.string);
258  return rhs ? lhs.char_string.compare(rhs) < 0 : lhs.char_string.compare("") < 0;
259  }
260 
261  template <typename CharT
262  , enable_if_char_array_t<CharT> = nullptr>
263  friend inline auto operator < (const FString& lhs, const CharT& rhs) -> bool
264  {
265  std::lock_guard<std::mutex> lock(lhs.char_string_mutex);
266  lhs.char_string = lhs.internal_toCharString(lhs.string);
267  return lhs.char_string.compare(rhs) < 0;
268  }
269 
270  template <typename CharT
271  , enable_if_wchar_ptr_t<CharT> = nullptr>
272  friend inline auto operator < (const FString& lhs, const CharT& rhs) -> bool
273  {
274  return rhs ? lhs.string.compare(rhs) < 0 : lhs.string.compare(L"") < 0;
275  }
276 
277  template <typename CharT
278  , enable_if_wchar_array_t<CharT> = nullptr>
279  friend inline auto operator < (const FString& lhs, const CharT& rhs) -> bool
280  {
281  return lhs.string.compare(rhs) < 0;
282  }
283 
284  friend inline auto operator <= (const FString& lhs, const FString& rhs) -> bool
285  {
286  return lhs.string <= rhs.string;
287  }
288 
289  template <typename CharT
290  , enable_if_char_ptr_t<CharT> = nullptr>
291  friend inline auto operator <= (const FString& lhs, const CharT& rhs) -> bool
292  {
293  std::lock_guard<std::mutex> lock(lhs.char_string_mutex);
294  lhs.char_string = lhs.internal_toCharString(lhs.string);
295  return rhs ? lhs.char_string.compare(rhs) <= 0 : lhs.char_string.compare("") <= 0;
296  }
297 
298  template <typename CharT
299  , enable_if_char_array_t<CharT> = nullptr>
300  friend inline auto operator <= (const FString& lhs, const CharT& rhs) -> bool
301  {
302  std::lock_guard<std::mutex> lock(lhs.char_string_mutex);
303  lhs.char_string = lhs.internal_toCharString(lhs.string);
304  return lhs.char_string.compare(rhs) <= 0;
305  }
306 
307  template <typename CharT
308  , enable_if_wchar_ptr_t<CharT> = nullptr>
309  friend inline auto operator <= (const FString& lhs, const CharT& rhs) -> bool
310  {
311  return rhs ? lhs.string.compare(rhs) <= 0 : lhs.string.compare(L"") <= 0;
312  }
313 
314  template <typename CharT
315  , enable_if_wchar_array_t<CharT> = nullptr>
316  friend inline auto operator <= (const FString& lhs, const CharT& rhs) -> bool
317  {
318  return lhs.string.compare(rhs) <= 0;
319  }
320 
321  friend inline auto operator == (const FString& lhs, const FString& rhs) -> bool
322  {
323  return lhs.string == rhs.string;
324  }
325 
326  template <typename CharT
327  , enable_if_char_ptr_t<CharT> = nullptr>
328  friend inline auto operator == (const FString& lhs, const CharT& rhs) -> bool
329  {
330  std::lock_guard<std::mutex> lock(lhs.char_string_mutex);
331  lhs.char_string = lhs.internal_toCharString(lhs.string);
332  return rhs ? lhs.char_string.compare(rhs) == 0 : lhs.char_string.compare("") == 0;
333  }
334 
335  template <typename CharT
336  , enable_if_char_array_t<CharT> = nullptr>
337  friend inline auto operator == (const FString& lhs, const CharT& rhs) -> bool
338  {
339  std::lock_guard<std::mutex> lock(lhs.char_string_mutex);
340  lhs.char_string = lhs.internal_toCharString(lhs.string);
341  return lhs.char_string.compare(rhs) == 0;
342  }
343 
344  template <typename CharT
345  , enable_if_wchar_ptr_t<CharT> = nullptr>
346  friend inline auto operator == (const FString& lhs, const CharT& rhs) -> bool
347  {
348  return rhs ? lhs.string.compare(rhs) == 0 : lhs.string.compare(L"") == 0;
349  }
350 
351  template <typename CharT
352  , enable_if_wchar_array_t<CharT> = nullptr>
353  friend inline auto operator == (const FString& lhs, const CharT& rhs) -> bool
354  {
355  return lhs.string.compare(rhs) == 0;
356  }
357 
358  friend inline auto operator != (const FString& lhs, const FString& rhs) -> bool
359  {
360  return ! ( lhs == rhs );
361  }
362 
363  template <typename CharT
364  , enable_if_char_ptr_t<CharT> = nullptr>
365  friend inline auto operator != (const FString& lhs, const CharT& rhs) -> bool
366  {
367  std::lock_guard<std::mutex> lock(lhs.char_string_mutex);
368  lhs.char_string = lhs.internal_toCharString(lhs.string);
369  return rhs ? lhs.char_string.compare(rhs) != 0 : lhs.char_string.compare("") != 0;
370  }
371 
372  template <typename CharT
373  , enable_if_char_array_t<CharT> = nullptr>
374  friend inline auto operator != (const FString& lhs, const CharT& rhs) -> bool
375  {
376  std::lock_guard<std::mutex> lock(lhs.char_string_mutex);
377  lhs.char_string = lhs.internal_toCharString(lhs.string);
378  return lhs.char_string.compare(rhs) != 0;
379  }
380 
381  template <typename CharT
382  , enable_if_wchar_ptr_t<CharT> = nullptr>
383  friend inline auto operator != (const FString& lhs, const CharT& rhs) -> bool
384  {
385  return rhs ? lhs.string.compare(rhs) != 0 : lhs.string.compare(L"") != 0;
386  }
387 
388  template <typename CharT
389  , enable_if_wchar_array_t<CharT> = nullptr>
390  friend inline auto operator != (const FString& lhs, const CharT& rhs) -> bool
391  {
392  return lhs.string.compare(rhs) != 0;
393  }
394 
395  friend inline auto operator >= (const FString& lhs, const FString& rhs) -> bool
396  {
397  return lhs.string >= rhs.string;
398  }
399 
400  template <typename CharT
401  , enable_if_char_ptr_t<CharT> = nullptr>
402  friend inline auto operator >= (const FString& lhs, const CharT& rhs) -> bool
403  {
404  std::lock_guard<std::mutex> lock(lhs.char_string_mutex);
405  lhs.char_string = lhs.internal_toCharString(lhs.string);
406  return rhs ? lhs.char_string.compare(rhs) >= 0 : lhs.char_string.compare("") >= 0;
407  }
408 
409  template <typename CharT
410  , enable_if_char_array_t<CharT> = nullptr>
411  friend inline auto operator >= (const FString& lhs, const CharT& rhs) -> bool
412  {
413  std::lock_guard<std::mutex> lock(lhs.char_string_mutex);
414  lhs.char_string = lhs.internal_toCharString(lhs.string);
415  return lhs.char_string.compare(rhs) >= 0;
416  }
417 
418  template <typename CharT
419  , enable_if_wchar_ptr_t<CharT> = nullptr>
420  friend inline auto operator >= (const FString& lhs, const CharT& rhs) -> bool
421  {
422  return rhs ? lhs.string.compare(rhs) >= 0 : lhs.string.compare(L"") >= 0;
423  }
424 
425  template <typename CharT
426  , enable_if_wchar_array_t<CharT> = nullptr>
427  friend inline auto operator >= (const FString& lhs, const CharT& rhs) -> bool
428  {
429  return lhs.string.compare(rhs) >= 0;
430  }
431 
432  friend inline auto operator > (const FString& lhs, const FString& rhs) -> bool
433  {
434  return lhs.string > rhs.string;
435  }
436 
437  template <typename CharT
438  , enable_if_char_ptr_t<CharT> = nullptr>
439  friend inline auto operator > (const FString& lhs, const CharT& rhs) -> bool
440  {
441  std::lock_guard<std::mutex> lock(lhs.char_string_mutex);
442  lhs.char_string = lhs.internal_toCharString(lhs.string);
443  return rhs ? lhs.char_string.compare(rhs) > 0 : lhs.char_string.compare("") > 0;
444  }
445 
446  template <typename CharT
447  , enable_if_char_array_t<CharT> = nullptr>
448  friend inline auto operator > (const FString& lhs, const CharT& rhs) -> bool
449  {
450  std::lock_guard<std::mutex> lock(lhs.char_string_mutex);
451  lhs.char_string = lhs.internal_toCharString(lhs.string);
452  return lhs.char_string.compare(rhs) > 0;
453  }
454 
455  template <typename CharT
456  , enable_if_wchar_ptr_t<CharT> = nullptr>
457  friend inline auto operator > (const FString& lhs, const CharT& rhs) -> bool
458  {
459  return rhs ? lhs.string.compare(rhs) > 0 : lhs.string.compare(L"") > 0;
460  }
461 
462  template <typename CharT
463  , enable_if_wchar_array_t<CharT> = nullptr>
464  friend inline auto operator > (const FString& lhs, const CharT& rhs) -> bool
465  {
466  return lhs.string.compare(rhs) > 0;
467  }
468 
469  // Accessors
470  virtual auto getClassName() const -> FString;
471  auto getLength() const noexcept -> size_type;
472  auto capacity() const noexcept -> size_type; // STL Compatibility
473  auto size() const noexcept -> size_type; // STL Compatibility
474  auto length() const noexcept -> size_type; // STL Compatibility
475  auto max_size() const noexcept -> size_type; // STL Compatibility
476 
477  // Predicate
478  auto isEmpty() const noexcept -> bool;
479 
480  // Iterators
481  auto begin() noexcept -> iterator;
482  auto end() noexcept -> iterator;
483  auto begin() const noexcept -> const_iterator;
484  auto end() const noexcept -> const_iterator;
485  auto cbegin() const noexcept -> const_iterator;
486  auto cend() const noexcept -> const_iterator;
487  auto rbegin() noexcept -> reverse_iterator;
488  auto rend() noexcept -> reverse_iterator;
489  auto crbegin() const noexcept -> const_reverse_iterator;
490  auto crend() const noexcept -> const_reverse_iterator;
491 
492  // Element access
493  auto front() -> reference;
494  auto back() -> reference;
495  auto front() const -> const_reference;
496  auto back() const -> const_reference;
497  auto at (size_type) -> reference; // STL Compatibility
498  auto at (size_type) const -> const_reference; // STL Compatibility
499 
500  // String operations
501  template <typename... Args>
502  auto sprintf (const FString&, Args&&...) -> FString&;
503  auto clear() noexcept -> FString&;
504  auto reserve (size_type) -> FString&; // STL Compatibility
505  auto shrink_to_fit() -> FString&; // STL Compatibility
506  auto resize (size_type) -> FString&; // STL Compatibility
507  auto resize (size_type, wchar_t) -> FString&; // STL Compatibility
508 
509  // wc-string and c-string conversion methods
510  auto wc_str() const noexcept -> const wchar_t*;
511  auto wc_str() noexcept -> wchar_t*;
512  auto c_str() const -> const char*;
513  auto c_str() -> char*;
514  auto data() const noexcept -> const wchar_t*; // STL Compatibility
515  auto data() noexcept -> wchar_t*; // STL Compatibility
516  auto toWString() const -> std::wstring;
517  auto toString() const -> std::string;
518 
519  // Case conversion
520  auto toLower() const -> FString;
521  auto toUpper() const -> FString;
522 
523  // Numeric conversions
524  auto toShort() const -> sInt16;
525  auto toUShort() const -> uInt16;
526  auto toInt() const -> int;
527  auto toUInt() const -> uInt;
528  auto toLong() const -> long;
529  auto toULong() const -> uLong;
530  auto toFloat() const -> float;
531  auto toDouble() const -> double;
532 
533  // String manipulation
534  auto ltrim() const -> FString;
535  auto rtrim() const -> FString;
536  auto trim() const -> FString;
537  auto left (size_type) const -> FString;
538  auto right (size_type) const -> FString;
539  auto mid (size_type, size_type) const -> FString;
540  auto split (const FString&) const -> FStringList;
541 
542  // Assignment methods
543  auto setString (const FString&) -> FString&;
544  template <typename NumT>
545  auto setNumber (NumT, int = int(getPrecision<NumT>())) -> FString&;
546  auto setNumber (sInt64) -> FString&;
547  auto setNumber (uInt64) -> FString&;
548  auto setNumber (lDouble, int = int(getPrecision<lDouble>())) -> FString&;
549 
550  template <typename NumT>
551  auto setFormatedNumber (NumT, FString&& = nl_langinfo(THOUSEP)) -> FString&;
552  auto setFormatedNumber (sInt64, FString&& = nl_langinfo(THOUSEP)) -> FString&;
553  auto setFormatedNumber (uInt64, FString&& = nl_langinfo(THOUSEP)) -> FString&;
554 
555  // String modification
556  auto insert (const FString&, int) -> const FString&;
557  auto insert (const FString&, size_type) -> const FString&;
558 
559  auto replace (const FString&, const FString&) const -> FString;
560 
561  auto replaceControlCodes() const -> FString;
562  auto expandTabs (int = 8) const -> FString;
563  auto removeDel() const -> FString;
564  auto removeBackspaces() const -> FString;
565 
566  auto overwrite (const FString&, int) -> FString&;
567  auto overwrite (const FString&, size_type = 0) -> FString&;
568 
569  auto remove (size_type = 0, size_type = npos) -> FString&;
570  auto erase (size_type pos = 0, size_type len = npos) -> FString&; // STL Compatibility
571 
572  // Search operations
573  auto includes (const FString&) const noexcept -> bool;
574  auto contains (const FString&) const noexcept -> bool; // STL Compatibility
575  auto find (const FString&, size_type = 0) const noexcept -> size_type; // STL Compatibility
576  auto rfind (const FString&, size_type = npos) const noexcept -> size_type; // STL Compatibility
577 
578  // Utility methods
579  void swap (FString&) noexcept;
580 
581  private:
582  // Enumeration
583  enum class Sign : bool { Negative, Positive };
584 
585  // Constants
586  static constexpr auto INPBUFFER = size_type(256);
587  static constexpr auto SPRINTF_BUFFER_SIZE = size_type(4096);
588  static constexpr auto NUMBER_BUFFER_SIZE = size_type(30);
589  static constexpr auto NUMBER_BUFFER_LENGTH = NUMBER_BUFFER_SIZE - 1;
590  static constexpr auto MALFORMED_STRING = size_type(-1);
591  static constexpr auto LONG_LIMIT = long(LONG_MAX / 10);
592  static constexpr auto LONG_MIN_LIMIT_DIGIT = long(-(LONG_MIN % 10) + 1);
593  static constexpr auto LONG_MAX_LIMIT_DIGIT = long(LONG_MAX % 10);
594  static constexpr auto ULONG_LIMIT = uLong(ULONG_MAX / 10);
595  static constexpr auto ULONG_LIMIT_DIGIT = uLong(ULONG_MAX % 10);
596 
597  // Methods
598  void internal_assign (std::wstring) noexcept;
599  auto internal_toCharString (const std::wstring&) const -> std::string;
600  auto internal_toWideString (const char[]) const -> std::wstring;
601  void internal_skipLeadingWs (const_iterator&, const_iterator) const noexcept;
602  auto internal_parseSign (const_iterator&) const noexcept -> Sign;
603  auto internal_parseDigits (const_iterator&, const_iterator, Sign) const -> long;
604  auto internal_parseDigits (const_iterator&, const_iterator) const -> uLong;
605  auto internal_isOverflowed (Sign, long, long) const noexcept -> bool;
606  auto internal_isOverflowed (uLong, uLong) const noexcept -> bool;
607  template <typename NumT>
608  auto internal_setFormatedNumber (NumT, FString) -> FString&;
609 
610  // Data members
611  std::wstring string{};
612  mutable std::string char_string{};
613  mutable std::mutex char_string_mutex{};
614  static wchar_t null_char;
615  static const wchar_t const_null_char;
616 
617  // Friend Non-member operator functions
618  friend auto operator + (const FString& s1, const FString& s2) -> FString
619  {
620  const auto& tmp = s1.string + s2.string;
621  return tmp;
622  }
623 
624  friend auto operator << (std::ostream& outstr, const FString& s) -> std::ostream&
625  {
626  const auto width = size_type(outstr.width());
627 
628  if ( s.string.length() > 0 )
629  {
630  outstr << s.internal_toCharString(s.string);
631  }
632  else if ( width > 0 )
633  {
634  const std::string fill_str(width, outstr.fill());
635  outstr << fill_str;
636  }
637 
638  return outstr;
639  }
640 
641  friend auto operator >> (std::istream& instr, FString& s) -> std::istream&
642  {
643  std::array<char, FString::INPBUFFER + 1> buf{};
644  instr.getline (buf.data(), FString::INPBUFFER);
645  auto wide_string = s.internal_toWideString(buf.data());
646 
647  if ( ! wide_string.empty() )
648  {
649  s.internal_assign (std::move(wide_string));
650  }
651 
652  return instr;
653  }
654 
655  friend auto operator << (std::wostream& outstr, const FString& s) -> std::wostream&
656  {
657  const auto width = size_type(outstr.width());
658 
659  if ( s.string.length() > 0 )
660  {
661  outstr << s.string;
662  }
663  else if ( width > 0 )
664  {
665  const std::wstring fill_str(width, outstr.fill());
666  outstr << fill_str;
667  }
668 
669  return outstr;
670  }
671 
672  friend auto operator >> (std::wistream& instr, FString& s) -> std::wistream&
673  {
674  std::array<wchar_t, FString::INPBUFFER + 1> buf{};
675  instr.getline (buf.data(), FString::INPBUFFER);
676  std::wstring str(buf.data());
677  s.internal_assign (std::move(str));
678  return instr;
679  }
680 
681  // Friend struct
682  friend struct std::hash<finalcut::FString>;
683 };
684 
685 // non-member function forward declarations
686 //----------------------------------------------------------------------
687 auto FStringCaseCompare (const FString&, const FString&) -> int;
688 
689 // FString inline functions
690 //----------------------------------------------------------------------
691 template <typename NumT
692  , std::enable_if_t< ( std::is_integral<NumT>::value
693  && ! std::is_same<NumT, bool>::value
694  && ! std::is_pointer<NumT>::value )
695  || ( std::is_floating_point<NumT>::value
696  && ! std::is_pointer<NumT>::value )
697  , int> >
698 inline auto FString::operator << (NumT value) -> FString&
699 {
700  const FString numstr(FString().setNumber(value));
701  string.append(numstr.string);
702  return *this;
703 }
704 
705 //----------------------------------------------------------------------
706 template <typename IndexT>
707 constexpr auto FString::operator [] (const IndexT pos) -> reference
708 {
709  if ( isNegative(pos) || pos > IndexT(string.length()) ) // Invalid index position
710  throw std::out_of_range("FString::operator[] index out of range");
711 
712  if ( size_type(pos) == string.length() )
713  return null_char;
714 
715  return string[size_type(pos)];
716 }
717 
718 //----------------------------------------------------------------------
719 template <typename IndexT>
720 constexpr auto FString::operator [] (const IndexT pos) const -> const_reference
721 {
722  if ( isNegative(pos) || pos > IndexT(string.length()) ) // Invalid index position
723  throw std::out_of_range("FString::operator[] index out of range");
724 
725  if ( size_type(pos) == string.length() )
726  return const_null_char;
727 
728  return string[size_type(pos)];
729 }
730 
731 //----------------------------------------------------------------------
732 inline auto FString::getClassName() const -> FString
733 { return "FString"; }
734 
735 //----------------------------------------------------------------------
736 inline auto FString::getLength() const noexcept -> size_type
737 { return string.length(); }
738 
739 //----------------------------------------------------------------------
740 inline auto FString::capacity() const noexcept -> size_type
741 { return string.capacity(); }
742 
743 //----------------------------------------------------------------------
744 inline auto FString::size() const noexcept -> size_type
745 { return string.size(); }
746 
747 //----------------------------------------------------------------------
748 inline auto FString::length() const noexcept -> size_type
749 { return string.length(); }
750 
751 //----------------------------------------------------------------------
752 inline auto FString::max_size() const noexcept -> size_type
753 { return string.max_size(); }
754 
755 //----------------------------------------------------------------------
756 inline auto FString::isEmpty() const noexcept -> bool
757 { return string.empty(); }
758 
759 //----------------------------------------------------------------------
760 inline auto FString::begin() noexcept -> iterator
761 { return string.begin(); }
762 
763 //----------------------------------------------------------------------
764 inline auto FString::end() noexcept -> iterator
765 { return string.end(); }
766 
767 //----------------------------------------------------------------------
768 inline auto FString::begin() const noexcept -> const_iterator
769 { return this->string.begin(); }
770 
771 //----------------------------------------------------------------------
772 inline auto FString::end() const noexcept -> const_iterator
773 { return this->string.end(); }
774 
775 //----------------------------------------------------------------------
776 inline auto FString::cbegin() const noexcept -> const_iterator
777 { return this->string.cbegin(); }
778 
779 //----------------------------------------------------------------------
780 inline auto FString::cend() const noexcept -> const_iterator
781 { return this->string.cend(); }
782 
783 //----------------------------------------------------------------------
784 inline auto FString::rbegin() noexcept -> reverse_iterator
785 { return string.rbegin(); }
786 
787 //----------------------------------------------------------------------
788 inline auto FString::rend() noexcept -> reverse_iterator
789 { return string.rend(); }
790 
791 //----------------------------------------------------------------------
792 inline auto FString::crbegin() const noexcept -> const_reverse_iterator
793 { return string.crbegin(); }
794 
795 //----------------------------------------------------------------------
796 inline auto FString::crend() const noexcept -> const_reverse_iterator
797 { return string.crend(); }
798 
799 //----------------------------------------------------------------------
800 inline auto FString::front() -> reference
801 {
802  assert ( ! isEmpty() && "FString::front() called on empty string" );
803  return string.front();
804 }
805 
806 //----------------------------------------------------------------------
807 inline auto FString::back() -> reference
808 {
809  assert( ! isEmpty() && "FString::back() called on empty string" );
810  return string.back();
811 }
812 
813 //----------------------------------------------------------------------
814 inline auto FString::front() const -> const_reference
815 {
816  assert ( ! isEmpty() && "FString::front() called on empty string" );
817  return string.front();
818 }
819 
820 //----------------------------------------------------------------------
821 inline auto FString::back() const -> const_reference
822 {
823  assert( ! isEmpty() && "FString::back() called on empty string" );
824  return string.back();
825 }
826 
827 //----------------------------------------------------------------------
828 inline auto FString::at (size_type pos) -> reference
829 { return string.at(pos); }
830 
831 //----------------------------------------------------------------------
832 inline auto FString::at (size_type pos) const -> const_reference
833 { return string.at(pos); }
834 
835 //----------------------------------------------------------------------
836 template <typename... Args>
837 inline auto FString::sprintf (const FString& format, Args&&... args) -> FString&
838 {
839  std::array<wchar_t, SPRINTF_BUFFER_SIZE> buf{};
840 
841  if ( format.isEmpty() )
842  {
843  clear();
844  return *this;
845  }
846 
847  const auto written = std::swprintf ( buf.data()
848  , buf.size()
849  , format.wc_str()
850  , std::forward<Args>(args)... );
851 
852  if ( written < 0 || size_type(written) >= buf.size() )
853  throw formatting_error("FString::sprintf buffer overflow or encoding error");
854 
855  return setString(buf.data());
856 }
857 
858 //----------------------------------------------------------------------
859 inline auto FString::data() const noexcept -> const wchar_t*
860 { return string.data(); }
861 
862 //----------------------------------------------------------------------
863 inline auto FString::data() noexcept -> wchar_t*
864 { return const_cast<wchar_t*>(string.data()); }
865 
866 //----------------------------------------------------------------------
867 template <typename NumT>
868 inline auto FString::setNumber (NumT value, int precision) -> FString&
869 {
870  if ( std::is_floating_point<NumT>::value )
871  return setNumber (lDouble(value), precision);
872 
873  if ( isNegative(value) )
874  return setNumber (sInt64(value));
875 
876  return setNumber (uInt64(value));
877 }
878 
879 //----------------------------------------------------------------------
880 template <typename NumT>
881 inline auto FString::setFormatedNumber (NumT value, FString&& separator) -> FString&
882 {
883  if ( isNegative(value) )
884  return setFormatedNumber (sInt64(value), std::move(separator));
885 
886  return setFormatedNumber (uInt64(value), std::move(separator));
887 }
888 
889 //----------------------------------------------------------------------
890 inline void FString::swap (FString& s) noexcept
891 {
892  s.string.swap(string);
893 }
894 
895 //----------------------------------------------------------------------
896 inline void FString::internal_assign (std::wstring s) noexcept
897 {
898  s.swap(string);
899 }
900 
901 } // namespace finalcut
902 
903 //----------------------------------------------------------------------
904 // Specialization of std::hash for FString
905 
906 namespace std
907 {
908 
909 template <>
910 struct hash<finalcut::FString>
911 {
912  auto operator () (const finalcut::FString& str) const noexcept -> std::size_t
913  {
914  return std::hash<std::wstring>{}(str.string);
915  }
916 };
917 
918 } // namespace std
919 
920 #endif // FSTRING_H
Definition: fstring.h:906
Definition: class_template.cpp:25
Definition: fstring.h:86
Definition: fstring.h:82
Definition: ftypes.h:152