AUI Framework  master
Cross-platform module-based framework for developing C++20 desktop applications
AString.h
1/*
2 * AUI Framework - Declarative UI toolkit for modern C++20
3 * Copyright (C) 2020-2024 Alex2772 and Contributors
4 *
5 * SPDX-License-Identifier: MPL-2.0
6 *
7 * This Source Code Form is subject to the terms of the Mozilla Public
8 * License, v. 2.0. If a copy of the MPL was not distributed with this
9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 */
11
12#pragma once
13
14#include <string>
15#include <iostream>
16#include "AUI/Core.h"
17#include "AUI/Traits/values.h"
18#include <AUI/Common/ASet.h>
19#include <optional>
20#include <AUI/Common/AOptional.h>
21#include <fmt/core.h>
22
23class API_AUI_CORE AStringVector;
24class API_AUI_CORE AByteBuffer;
25class API_AUI_CORE AByteBufferView;
26
36class API_AUI_CORE AString: std::u16string
37{
38private:
39 friend struct std::hash<AString>;
40 using super = std::u16string;
41
42public:
43
44 using iterator = super::iterator;
45 using value_type = super::value_type;
46 using const_iterator = super::const_iterator;
47 using reverse_iterator = super::reverse_iterator;
48 using const_reverse_iterator = super::const_reverse_iterator;
49 auto constexpr static NPOS = super::npos;
50
51
52 AString(AString&& other) noexcept
53 : std::u16string(static_cast<basic_string&&>(other))
54 {
55 }
56
60 AString(const basic_string& other) noexcept
61 : basic_string<char16_t>(other)
62 {
63 }
64
68 AString(const std::string& utf8) noexcept;
69
70 AString(const AString& other) noexcept
71 : super(other.c_str())
72 {
73 }
74
75 AString(const basic_string& rhs, const std::allocator<char16_t>& allocator) noexcept
76 : basic_string<char16_t>(rhs, allocator)
77 {
78 }
79
80 template <class Iterator>
81 AString(Iterator first, Iterator last) noexcept : super(first, last) {}
82
83 AString() noexcept
84 {
85 }
86
87 AString(char16_t c) noexcept : super(&c, &c + 1)
88 {
89
90 }
91
95 AString(const char* utf8) noexcept;
96
100 AString(std::string_view utf8) noexcept;
101
102 explicit AString(const std::allocator<char16_t>& allocator) noexcept
103 : basic_string<char16_t>(allocator)
104 {
105 }
106
107 AString(const basic_string& rhs, size_type offset, const std::allocator<char16_t>& allocator) noexcept
108 : basic_string<char16_t>(rhs, offset, allocator)
109 {
110 }
111
112 AString(const basic_string& rhs, size_type offset, size_type count, const std::allocator<char16_t>& allocator) noexcept
113 : basic_string<char16_t>(rhs, offset, count, allocator)
114 {
115 }
116
117 AString(const char16_t* cStyleString, size_type count) noexcept
118 : basic_string<char16_t>(cStyleString, count)
119 {
120 }
121
122 AString(const char16_t* cStyleString, size_type count, const std::allocator<char16_t>& allocator) noexcept
123 : basic_string<char16_t>(cStyleString, count, allocator)
124 {
125 }
126
127 AString(const char16_t* cStyleString) noexcept
128 : basic_string<char16_t>(cStyleString)
129 {
130 }
131
132 AString(const char16_t* cStyleString, const std::allocator<char16_t>& allocator) noexcept
133 : basic_string<char16_t>(cStyleString, allocator)
134 {
135 }
136
137 AString(size_type count, char16_t _Ch) noexcept
138 : basic_string<char16_t>(count, _Ch)
139 {
140 }
141
142 AString(size_type count, char16_t _Ch, const std::allocator<char16_t>& allocator) noexcept
143 : basic_string<char16_t>(count, _Ch, allocator)
144 {
145 }
146
147 AString(basic_string&& rhs) noexcept
148 : basic_string<char16_t>(std::move(rhs))
149 {
150 }
151
152 AString(basic_string&& rhs, const std::allocator<char16_t>& allocator) noexcept
153 : basic_string<char16_t>(std::move(rhs), allocator)
154 {
155 }
156
157 AString(std::initializer_list<char16_t> _Ilist) noexcept
158 : basic_string<char16_t>(_Ilist)
159 {
160 }
161
162 ~AString() = default;
163
164
165 void push_back(char16_t c) noexcept
166 {
167 super::push_back(c);
168 }
169 void pop_back() noexcept
170 {
171 super::pop_back();
172 }
173
174 AString uppercase() const;
175 AString lowercase() const;
176
177 bool startsWith(const AString& other) const noexcept
178 {
179 return rfind(other, 0) == 0;
180 }
181 bool startsWith(char16_t c) const noexcept
182 {
183 return rfind(c, 0) == 0;
184 }
185 bool endsWith(const AString& other) const noexcept
186 {
187 if (length() < other.length())
188 {
189 return false;
190 }
191 size_t offset = length() - other.length();
192 return super::find(other, offset) == offset;
193 }
194 bool endsWith(char16_t c) const noexcept
195 {
196 size_t offset = length() - 1;
197 return super::find(c, offset) == offset;
198 }
199
200 AStringVector split(char16_t c) const noexcept;
201
202 size_type find(char c, size_type offset = 0) const noexcept
203 {
204 return super::find(c, offset);
205 }
206 size_type find(char16_t c, size_type offset = 0) const noexcept
207 {
208 return super::find(c, offset);
209 }
210 size_type find(const AString& str, size_type offset = 0) const noexcept
211 {
212 return super::find(str, offset);
213 }
214 size_type rfind(char c, size_type offset = NPOS) const noexcept
215 {
216 return super::rfind(c, offset);
217 }
218 size_type rfind(char16_t c, size_type offset = NPOS) const noexcept
219 {
220 return super::rfind(c, offset);
221 }
222 size_type rfind(const AString& str, size_type offset = NPOS) const noexcept
223 {
224 return super::rfind(str, offset);
225 }
226 size_type length() const noexcept
227 {
228 return super::length();
229 }
230 AString trimLeft(char16_t symbol = ' ') const noexcept;
231 AString trimRight(char16_t symbol = ' ') const noexcept;
232
233 AString trim(char16_t symbol = ' ') const noexcept
234 {
235 return trimRight(symbol).trimLeft(symbol);
236 }
237
238 void reserve(size_t s)
239 {
240 super::reserve(s);
241 }
242 void resize(size_t s)
243 {
244 super::resize(s);
245 }
246
247 AString restrictLength(size_t s, const AString& stringAtEnd = "...") const;
248
249 char16_t* data() noexcept
250 {
251 return super::data();
252 }
253
254 const char16_t* data() const noexcept
255 {
256 return super::data();
257 }
258 AString& replaceAll(const AString& from, const AString& to);
259 [[nodiscard]] AString replacedAll(const AString& from, const AString& to) const;
260 [[nodiscard]] inline AString replacedAll(char16_t from, char16_t to) const noexcept {
261 AString copy;
262 copy.reserve(length() + 10);
263 for (auto c : *this) {
264 if (c == from) {
265 copy << to;
266 } else {
267 copy << c;
268 }
269 }
270 return copy;
271 }
272 [[nodiscard]] inline AString replacedAll(const ASet<char16_t>& from, char16_t to) const noexcept {
273 AString copy;
274 copy.reserve(length() + 10);
275 for (auto c : *this) {
276 if (from.contains(c)) {
277 copy << to;
278 } else {
279 copy << c;
280 }
281 }
282 return copy;
283 }
284 AString& replaceAll(char16_t from, char16_t to) noexcept;
285
286
293 template<typename OtherContainer>
294 void insertAll(const OtherContainer& c) noexcept {
295 super::insert(super::end(), c.begin(), c.end());
296 }
297
304 [[nodiscard]]
305 AOptional<float> toFloat() const noexcept;
306
313 [[nodiscard]]
314 AOptional<double> toDouble() const noexcept;
315
322 [[nodiscard]]
323 double toDoubleOrException() const noexcept {
324 return toDouble().valueOrException(fmt::format("bad double: {}", toStdString()).c_str());
325 }
326
334 [[nodiscard]]
335 AOptional<int> toInt() const noexcept;
336
344 [[nodiscard]]
345 int toIntOrException() const {
346 return toInt().valueOrException(fmt::format("bad int: {}", toStdString()).c_str());
347 }
348
356 [[nodiscard]]
357 AOptional<int64_t> toLongInt() const noexcept;
358
366 [[nodiscard]]
367 int64_t toLongIntOrException() const {
368 return toLongInt().valueOrException(fmt::format("bad to number conversion: {}", toStdString()).c_str());
369 }
370
378 [[nodiscard]]
379 AOptional<unsigned> toUInt() const noexcept;
380
388 [[nodiscard]]
389 unsigned toUIntOrException() const {
390 return toUInt().valueOrException(fmt::format("bad to number conversion: {}", toStdString()).c_str());
391 }
392
397 [[nodiscard]]
398 bool toBool() const noexcept {
399 return *this == "true";
400 }
401
402 [[nodiscard]]
403 bool contains(char16_t c) const noexcept
404 {
405 return find(c) != npos;
406 }
407 [[nodiscard]]
408 bool contains(const AString& other) const noexcept
409 {
410 return find(other) != npos;
411 }
412
413 static AString fromLatin1(const AByteBuffer& buffer);
414 static AString fromUtf8(const AByteBufferView& buffer);
415 static AString fromUtf8(const char* buffer, size_t length);
416 static AString fromLatin1(const char* buffer);
417
418 static AString numberHex(int i) noexcept;
419
420 template<typename T, std::enable_if_t<std::is_integral_v<std::decay_t<T>> || std::is_floating_point_v<std::decay_t<T>>, int> = 0>
421 static AString number(T i) noexcept {
422 if constexpr (std::is_same_v<bool, std::decay_t<T>>) {
423 if (i)
424 return "true";
425 return "false";
426 } else {
427 auto v = std::to_string(i);
428 if constexpr (std::is_floating_point_v<T>) {
429 // remove trailing zeros
430 v.erase(v.find_last_not_of('0') + 1, std::u16string::npos);
431 v.erase(v.find_last_not_of('.') + 1, std::u16string::npos);
432 }
433 return v;
434 }
435 }
436
437 static constexpr auto TO_NUMBER_BASE_BIN = 2;
438 static constexpr auto TO_NUMBER_BASE_OCT = 8;
439 static constexpr auto TO_NUMBER_BASE_DEC = 10;
440 static constexpr auto TO_NUMBER_BASE_HEX = 16;
441
442
447 AOptional<int> toNumber(aui::ranged_number<int, 2, 36> base = TO_NUMBER_BASE_DEC) const noexcept;
448
453 int toNumberOrException(aui::ranged_number<int, 2, 36> base = TO_NUMBER_BASE_DEC) const {
454 return toNumber(base).valueOrException(fmt::format("bad to number conversion: {}", toStdString()).c_str());
455 }
456
457
461 std::string toStdString() const noexcept;
462
463 void resizeToNullTerminator();
464
465 iterator erase(const_iterator begin, const_iterator end) noexcept
466 {
467 return super::erase(begin, end);
468 }
469 iterator erase(const_iterator begin) noexcept
470 {
471 return super::erase(begin);
472 }
473
474 AString& erase(size_type offset) noexcept
475 {
476 super::erase(offset);
477 return *this;
478 }
479 AString& erase(size_type offset, size_type count) noexcept
480 {
481 super::erase(offset, count);
482 return *this;
483 }
484
485 AByteBuffer toUtf8() const noexcept;
486
487 void removeAt(unsigned at) noexcept
488 {
489 AUI_ASSERT(at <= length());
490 erase(begin() + at);
491 }
492 AString excessSpacesRemoved() const noexcept;
493
494 iterator insert(size_type at, char16_t c) noexcept
495 {
496 AUI_ASSERT(at <= length());
497 return super::insert(begin() + at, 1, c);
498 }
499 iterator insert(size_type at, const AString& c) noexcept
500 {
501 AUI_ASSERT(at <= length());
502 return super::insert(begin() + at, c.begin(), c.end());
503 }
504
505 template<typename Iterator>
506 iterator insert(const_iterator at, Iterator begin, Iterator end) noexcept
507 {
508 AUI_ASSERT(std::distance(super::cbegin(), at) <= length());
509 return super::insert(at, begin, end);
510 }
511
512 AString& operator<<(char c) noexcept
513 {
514 append(1, c);
515 return *this;
516 }
517 AString& operator<<(char16_t c) noexcept
518 {
519 append(1, c);
520 return *this;
521 }
522
523 inline ::AString& operator+=(const AString& str) noexcept
524 {
525 append(str);
526 return *this;
527 }
528 inline ::AString& operator+=(const char* str) noexcept
529 {
530 *this += AString(str);
531 return *this;
532 }
533
534 [[nodiscard]] bool empty() const noexcept {
535 return super::empty();
536 }
537 [[nodiscard]] size_type size() const noexcept {
538 return super::size();
539 }
540 char16_t operator[](size_type index) const
541 {
542 return super::at(index);
543 }
544 char16_t& operator[](size_type index)
545 {
546 return super::at(index);
547 }
548 bool operator<(const AString& other) const noexcept
549 {
550 return compare(other) < 0;
551 }
552
553 void clear() noexcept
554 {
555 super::clear();
556 }
557
558 char16_t& front() noexcept
559 {
560 return super::front();
561 }
562 char16_t& back() noexcept
563 {
564 return super::back();
565 }
566 const char16_t& front() const noexcept
567 {
568 return super::front();
569 }
570 const char16_t& back() const noexcept
571 {
572 return super::back();
573 }
574 char16_t& first() noexcept
575 {
576 return super::front();
577 }
578 char16_t& last() noexcept
579 {
580 return super::back();
581 }
582 const char16_t& first() const noexcept
583 {
584 return super::front();
585 }
586 const char16_t& last() const noexcept
587 {
588 return super::back();
589 }
590
591 const char16_t* c_str() const
592 {
593 return super::c_str();
594 }
595
596 iterator begin() noexcept
597 {
598 return super::begin();
599 }
600 iterator end() noexcept
601 {
602 return super::end();
603 }
604
605 const_iterator begin() const noexcept
606 {
607 return super::begin();
608 }
609 const_iterator end() const noexcept
610 {
611 return super::end();
612 }
613
614 reverse_iterator rbegin() noexcept
615 {
616 return super::rbegin();
617 }
618 reverse_iterator rend() noexcept
619 {
620 return super::rend();
621 }
622
623 const_reverse_iterator rbegin() const noexcept
624 {
625 return super::rbegin();
626 }
627 const_reverse_iterator rend() const noexcept
628 {
629 return super::rend();
630 }
631
632 AString& append(const AString& s) noexcept
633 {
634 super::append(s);
635 return *this;
636 }
637
638 AString& append(size_t count, char16_t ch) noexcept
639 {
640 super::append(count, ch);
641 return *this;
642 }
643
644 const AString& operator=(const AString& value) noexcept
645 {
646 super::operator=(value);
647 return *this;
648 }
649
650 const AString& operator=(AString&& value) noexcept
651 {
652 super::operator=(value);
653 return *this;
654 }
655
656 bool operator==(const AString& other) const noexcept
657 {
658 if (size() != other.size()) {
659 return false;
660 }
661 return std::memcmp(data(), other.data(), sizeInBytes()) == 0;
662 }
663 bool operator==(const char16_t* other) const noexcept
664 {
665 auto it = begin();
666 for (; it != end(); ++it, ++other) {
667 if (*it != *other) {
668 return false;
669 }
670 if (*other == '\0') {
671 return false;
672 }
673 }
674 return *other == '\0';
675 }
676
677 [[nodiscard]]
678 size_t sizeInBytes() const noexcept {
679 return size() * sizeof(super::value_type);
680 }
681
682 bool operator!=(const AString& other) const noexcept
683 {
684 return !operator==(other);
685 }
686 bool operator!=(const char16_t* other) const noexcept
687 {
688 return !operator==(other);
689 }
690
691 bool operator==(const char* other) const noexcept
692 {
693 return *this == AString(other);
694 }
695
696 bool operator!=(const char* other) const noexcept
697 {
698 return *this != AString(other);
699 }
700
701 template<typename... Args>
702 inline AString format(Args&&... args) const;
703
704 AString processEscapes() const;
705
706 AString& removeAll(char16_t c) noexcept {
707 erase(std::remove(begin(), end(), c));
708 return *this;
709 }
710
711 [[nodiscard]]
712 AString substr(std::size_t offset, std::size_t count = npos) const {
713 return super::substr(offset, count);
714 }
715
716private:
720 template<typename T>
721 AOptional<T> toNumberImpl() const noexcept;
722};
723
724inline AString operator+(const AString& l, const AString& r) noexcept
725{
726 auto x = l;
727 x.append(r);
728 return x;
729}
730inline AString operator+(const AString& l, char16_t r) noexcept
731{
732 auto x = l;
733 x.append(r);
734 return x;
735}
736inline AString operator+(const AString& one, const char* other) noexcept
737{
738 return one + AString(other);
739}
740
741inline AString operator+(const char* other, const AString& one) noexcept {
742 return AString(other) + one;
743}
744
745inline AString operator+(char lhs, const AString& cs) noexcept
746{
747 AString s(lhs);
748 s += cs;
749 return s;
750}
751
752inline AString operator""_as(const char* str, size_t len)
753{
754 return {str};
755}
756
757inline std::ostream& operator<<(std::ostream& o, const AString& s)
758{
759 o << s.toStdString();
760 return o;
761}
762
763template<>
764struct std::hash<AString>
765{
766 size_t operator()(const AString& t) const
767 {
768 return std::hash<std::u16string>()(t);
769 }
770};
771
772#if AUI_PLATFORM_WIN
773namespace aui::win32 {
774 /*
775 * On Windows, char16_t == wchar_t. WinAPI interfaces use wchar_t widely, so we have some handy functions to
776 * convert AString to wchar_t* and back.
777 */
778
779 inline const wchar_t* toWchar(const AString& string) {
780 // NOLINTNEXTLINE(*-pro-type-reinterpret-cast)
781 return reinterpret_cast<const wchar_t *const>(string.data());
782 }
783
784 inline wchar_t* toWchar(AString& string) {
785 // NOLINTNEXTLINE(*-pro-type-reinterpret-cast)
786 return reinterpret_cast<wchar_t*>(string.data());
787 }
788
789 inline std::wstring_view toWcharView(const AString& string) {
790 return {toWchar(string), string.length() };
791 }
792
793 inline AString fromWchar(std::wstring_view string) {
794 // NOLINTNEXTLINE(*-pro-type-reinterpret-cast)
795 return {reinterpret_cast<const char16_t *>(string.data()), string.size()};
796 }
797}
798#endif
799
800template <> struct fmt::detail::is_string<AString>: std::false_type {};
801
802template <> struct fmt::formatter<AString>: fmt::formatter<std::string> {
803 auto format(const AString& s, format_context& ctx) const {
804 return fmt::formatter<std::string>::format(s.toStdString(), ctx);
805 }
806};
807
808
809// gtest printer for AString
810inline void PrintTo(const AString& s, std::ostream* stream) {
811 *stream << s.toStdString();
812}
Acts like std::string_view but for AByteBuffer.
Definition: AByteBufferView.h:24
std::vector-like growing array for byte storage.
Definition: AByteBuffer.h:31
Utility wrapper implementing the stack-allocated (fast) optional idiom.
Definition: AOptional.h:32
T & valueOrException(const char *message="empty optional")
value or exception
Definition: AOptional.h:207
A std::set with AUI extensions.
Definition: ASet.h:25
An AVector with string-related functions.
Definition: AStringVector.h:22
Represents a Unicode character string.
Definition: AString.h:37
AString(const basic_string &other) noexcept
Definition: AString.h:60
bool toBool() const noexcept
Converts the string to boolean value.
Definition: AString.h:398
void insertAll(const OtherContainer &c) noexcept
Definition: AString.h:294
std::string toStdString() const noexcept
Definition: AString.cpp:338
int toNumberOrException(aui::ranged_number< int, 2, 36 > base=TO_NUMBER_BASE_DEC) const
Returns the string converted to an int using base. Throws an exception if the conversion fails.
Definition: AString.h:453
API_AUI_CORE const ACommandLineArgs & args() noexcept
Definition: OSAndroid.cpp:29
bool contains(const Container &c, const typename Container::const_reference value) noexcept
Definition: containers.h:153
#define AUI_ASSERT(condition)
Asserts that the passed condition evaluates to true.
Definition: Assert.h:55
Definition: Empty.h:19
Clamps the possible values for a number to the specified range: [min;max].
Definition: values.h:452