/src/CMake/Source/cmString.hxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #pragma once |
4 | | |
5 | | #include "cmConfigure.h" // IWYU pragma: keep |
6 | | |
7 | | #include <algorithm> |
8 | | #include <cstddef> |
9 | | #include <initializer_list> |
10 | | #include <memory> |
11 | | #include <ostream> |
12 | | #include <string> |
13 | | #include <type_traits> |
14 | | #include <utility> |
15 | | |
16 | | #include <cm/string_view> |
17 | | #include <cmext/string_view> |
18 | | |
19 | | namespace cm { |
20 | | |
21 | | class String; |
22 | | |
23 | | /** |
24 | | * Trait to convert type T into a String. |
25 | | * Implementations must derive from 'std::true_type' |
26 | | * and define an 'into_string' member that accepts |
27 | | * type T (by value or reference) and returns one of: |
28 | | * |
29 | | * - 'std::string' to construct an owned instance. |
30 | | * - 'cm::string_view' to construct a borrowed or null instances. |
31 | | * The buffer from which the view is borrowed must outlive |
32 | | * all copies of the resulting String, e.g. static storage. |
33 | | * - 'cm::String' for already-constructed instances. |
34 | | */ |
35 | | template <typename T> |
36 | | struct IntoString : std::false_type |
37 | | { |
38 | | }; |
39 | | |
40 | | template <typename T> |
41 | | struct IntoString<T&> : IntoString<T> |
42 | | { |
43 | | }; |
44 | | |
45 | | template <typename T> |
46 | | struct IntoString<T const> : IntoString<T> |
47 | | { |
48 | | }; |
49 | | |
50 | | template <typename T> |
51 | | struct IntoString<T const*> : IntoString<T*> |
52 | | { |
53 | | }; |
54 | | |
55 | | template <typename T, std::string::size_type N> |
56 | | struct IntoString<T const[N]> : IntoString<T[N]> |
57 | | { |
58 | | }; |
59 | | |
60 | | template <> |
61 | | struct IntoString<char*> : std::true_type |
62 | | { |
63 | | static String into_string(char const* s); |
64 | | }; |
65 | | |
66 | | template <> |
67 | | struct IntoString<std::nullptr_t> : std::true_type |
68 | | { |
69 | 0 | static string_view into_string(std::nullptr_t) { return string_view(); } |
70 | | }; |
71 | | |
72 | | template <std::string::size_type N> |
73 | | struct IntoString<char[N]> : std::true_type |
74 | | { |
75 | | static std::string into_string(char const (&s)[N]) |
76 | | { |
77 | | return std::string(s, N - 1); |
78 | | } |
79 | | }; |
80 | | |
81 | | template <> |
82 | | struct IntoString<std::string> : std::true_type |
83 | | { |
84 | 98 | static std::string into_string(std::string s) { return s; } |
85 | | }; |
86 | | |
87 | | template <> |
88 | | struct IntoString<char> : std::true_type |
89 | | { |
90 | 0 | static std::string into_string(char c) { return std::string(1, c); } |
91 | | }; |
92 | | |
93 | | /** |
94 | | * Trait to convert type T into a 'cm::string_view'. |
95 | | * Implementations must derive from 'std::true_type' and |
96 | | * define a 'view' member that accepts type T (by reference) |
97 | | * and returns a 'cm::string_view'. |
98 | | */ |
99 | | template <typename T> |
100 | | struct AsStringView : std::false_type |
101 | | { |
102 | | }; |
103 | | |
104 | | template <typename T> |
105 | | struct AsStringView<T&> : AsStringView<T> |
106 | | { |
107 | | }; |
108 | | |
109 | | template <typename T> |
110 | | struct AsStringView<T const> : AsStringView<T> |
111 | | { |
112 | | }; |
113 | | |
114 | | template <typename T> |
115 | | struct AsStringView<T const*> : AsStringView<T*> |
116 | | { |
117 | | }; |
118 | | |
119 | | template <typename T, std::string::size_type N> |
120 | | struct AsStringView<T const[N]> : AsStringView<T[N]> |
121 | | { |
122 | | }; |
123 | | |
124 | | template <> |
125 | | struct AsStringView<char*> : std::true_type |
126 | | { |
127 | 0 | static string_view view(char const* s) { return s; } |
128 | | }; |
129 | | |
130 | | template <std::string::size_type N> |
131 | | struct AsStringView<char[N]> : std::true_type |
132 | | { |
133 | | static string_view view(char const (&s)[N]) { return string_view(s, N - 1); } |
134 | | }; |
135 | | |
136 | | template <> |
137 | | struct AsStringView<std::string> : std::true_type |
138 | | { |
139 | 0 | static string_view view(std::string const& s) { return s; } |
140 | | }; |
141 | | |
142 | | template <> |
143 | | struct AsStringView<char> : std::true_type |
144 | | { |
145 | | static string_view view( |
146 | | char const& s) // clazy:exclude=function-args-by-value |
147 | 0 | { |
148 | 0 | return string_view(&s, 1); |
149 | 0 | } |
150 | | }; |
151 | | |
152 | | template <> |
153 | | struct AsStringView<string_view> : std::true_type |
154 | | { |
155 | 0 | static string_view view(string_view s) { return s; } |
156 | | }; |
157 | | |
158 | | template <> |
159 | | struct AsStringView<static_string_view> : std::true_type |
160 | | { |
161 | | static string_view view( |
162 | | static_string_view const& s) // clazy:exclude=function-args-by-value |
163 | 0 | { |
164 | 0 | return s; |
165 | 0 | } |
166 | | }; |
167 | | |
168 | | template <> |
169 | | struct AsStringView<String> : std::true_type |
170 | | { |
171 | | static string_view view(String const& s); |
172 | | }; |
173 | | |
174 | | /** |
175 | | * \class String |
176 | | * |
177 | | * A custom string type that holds a view of a string buffer |
178 | | * and optionally shares ownership of the buffer. Instances |
179 | | * may have one of the following states: |
180 | | * |
181 | | * - null: views and owns nothing. |
182 | | * Conversion to 'bool' is 'false'. |
183 | | * 'data()' and 'c_str()' return nullptr. |
184 | | * 'size()' returns 0. |
185 | | * 'str()' returns an empty string. |
186 | | * |
187 | | * - borrowed: views a string but does not own it. This is used |
188 | | * to bind to static storage (e.g. string literals) or for |
189 | | * temporary instances that do not outlive the borrowed buffer. |
190 | | * Copies and substrings still borrow the original buffer. |
191 | | * Mutation allocates a new internal string and converts to |
192 | | * the 'owned' state. |
193 | | * Conversion to 'bool' is 'true'. |
194 | | * 'c_str()' may internally mutate to the 'owned' state. |
195 | | * 'str()' internally mutates to the 'owned' state. |
196 | | * |
197 | | * - owned: views an immutable 'std::string' instance owned internally. |
198 | | * Copies and substrings share ownership of the internal string. |
199 | | * Mutation allocates a new internal string. |
200 | | * Conversion to 'bool' is 'true'. |
201 | | */ |
202 | | class String |
203 | | { |
204 | | enum class Private |
205 | | { |
206 | | }; |
207 | | |
208 | | public: |
209 | | using traits_type = std::string::traits_type; |
210 | | using value_type = string_view::value_type; |
211 | | using pointer = string_view::pointer; |
212 | | using const_pointer = string_view::const_pointer; |
213 | | using reference = string_view::reference; |
214 | | using const_reference = string_view::const_reference; |
215 | | using const_iterator = string_view::const_iterator; |
216 | | using iterator = string_view::const_iterator; |
217 | | using const_reverse_iterator = string_view::const_reverse_iterator; |
218 | | using reverse_iterator = string_view::const_reverse_iterator; |
219 | | using difference_type = string_view::difference_type; |
220 | | using size_type = string_view::size_type; |
221 | | |
222 | | static size_type const npos = string_view::npos; |
223 | | |
224 | | /** Construct a null string. */ |
225 | 95 | String() = default; |
226 | | |
227 | | /** Construct from any type implementing the IntoString trait. */ |
228 | | template <typename T, |
229 | | typename = typename std::enable_if<IntoString<T>::value>::type> |
230 | | String(T&& s) |
231 | 98 | : String(IntoString<T>::into_string(std::forward<T>(s)), Private()) |
232 | 98 | { |
233 | 98 | } Unexecuted instantiation: cm::String::String<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, void>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&) Unexecuted instantiation: cm::String::String<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, void>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) Unexecuted instantiation: cm::String::String<char*, void>(char*&&) cm::String::String<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, void>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 231 | 98 | : String(IntoString<T>::into_string(std::forward<T>(s)), Private()) | 232 | 98 | { | 233 | 98 | } |
|
234 | | |
235 | | /** |
236 | | * Construct via static_string_view constructor. |
237 | | * explicit is required to avoid ambiguous overloaded operators (i.e ==, |
238 | | * etc...) with the ones provided by string_view. |
239 | | */ |
240 | | explicit String(static_string_view s) |
241 | | : String(s, Private()) |
242 | 0 | { |
243 | 0 | } |
244 | | /** |
245 | | * Construct via string_view constructor. |
246 | | * explicit is required to avoid ambiguous overloaded operators (i.e ==, |
247 | | * etc...) with the ones provided by string_view. |
248 | | */ |
249 | | explicit String(string_view s) |
250 | 97 | : String(std::string(s), Private()) |
251 | 97 | { |
252 | 97 | } |
253 | | |
254 | | /** Construct via std::string initializer list constructor. */ |
255 | | String(std::initializer_list<char> il) |
256 | | : String(std::string(il)) |
257 | 0 | { |
258 | 0 | } |
259 | | |
260 | | /** Construct by copying the specified buffer. */ |
261 | | String(char const* d, size_type s) |
262 | 0 | : String(std::string(d, s)) |
263 | 0 | { |
264 | 0 | } |
265 | | |
266 | | /** Construct by copying from input iterator range. */ |
267 | | template <typename InputIterator> |
268 | | String(InputIterator first, InputIterator last) |
269 | | : String(std::string(first, last)) |
270 | | { |
271 | | } |
272 | | |
273 | | /** Construct a string with 'n' copies of character 'c'. */ |
274 | | String(size_type n, char c) |
275 | | : String(std::string(n, c)) |
276 | 0 | { |
277 | 0 | } |
278 | | |
279 | | /** Construct from a substring of another String instance. |
280 | | This shares ownership of the other string's buffer |
281 | | but views only a substring. */ |
282 | | String(String const& s, size_type pos, size_type count = npos) |
283 | 0 | : string_(s.string_) |
284 | 0 | , view_(s.data() + pos, std::min(count, s.size() - pos)) |
285 | 0 | { |
286 | 0 | } |
287 | | |
288 | | /** Construct by moving from another String instance. |
289 | | The other instance is left as a null string. */ |
290 | | String(String&& s) noexcept |
291 | 94 | : string_(std::move(s.string_)) |
292 | 94 | , view_(s.view_) |
293 | 94 | { |
294 | 94 | s.view_ = string_view(); |
295 | 94 | } |
296 | | |
297 | | /** Construct by copying from another String instance. |
298 | | This shares ownership of the other string's buffer. */ |
299 | 0 | String(String const&) noexcept = default; |
300 | | |
301 | 392 | ~String() = default; |
302 | | |
303 | | /** Construct by borrowing an externally-owned buffer. The buffer |
304 | | must outlive the returned instance and all copies of it. */ |
305 | 8 | static String borrow(string_view v) { return String(v, Private()); } |
306 | | |
307 | | /** Assign by moving from another String instance. |
308 | | The other instance is left as a null string. */ |
309 | | String& operator=(String&& s) noexcept |
310 | 98 | { |
311 | 98 | this->string_ = std::move(s.string_); |
312 | 98 | this->view_ = s.view_; |
313 | 98 | s.view_ = string_view(); |
314 | 98 | return *this; |
315 | 98 | } |
316 | | |
317 | | /** Assign by copying from another String instance. |
318 | | This shares ownership of the other string's buffer. */ |
319 | | String& operator=(String const&) noexcept = default; |
320 | | |
321 | | String& operator=(static_string_view s) |
322 | 0 | { |
323 | 0 | *this = String(s); |
324 | 0 | return *this; |
325 | 0 | } |
326 | | String& operator=(string_view s) |
327 | 0 | { |
328 | 0 | *this = String(s); |
329 | 0 | return *this; |
330 | 0 | } |
331 | | |
332 | | /** Assign from any type implementing the IntoString trait. */ |
333 | | template <typename T> |
334 | | typename // NOLINT(*) |
335 | | std::enable_if<IntoString<T>::value, String&>::type |
336 | | operator=(T&& s) |
337 | 0 | { |
338 | 0 | *this = String(std::forward<T>(s)); |
339 | 0 | return *this; |
340 | 0 | } Unexecuted instantiation: _ZN2cm6StringaSINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEENS2_9enable_ifIXsr10IntoStringIT_EE5valueERS0_E4typeEOSA_ Unexecuted instantiation: _ZN2cm6StringaSIRNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEENS2_9enable_ifIXsr10IntoStringIT_EE5valueERS0_E4typeEOSB_ Unexecuted instantiation: _ZN2cm6StringaSIPcEENSt3__19enable_ifIXsr10IntoStringIT_EE5valueERS0_E4typeEOS5_ |
341 | | |
342 | | /** Assign via std::string initializer list constructor. */ |
343 | | String& operator=(std::initializer_list<char> il) |
344 | 0 | { |
345 | 0 | *this = String(il); |
346 | 0 | return *this; |
347 | 0 | } |
348 | | |
349 | | /** Return true if the instance is not a null string. */ |
350 | 8 | explicit operator bool() const noexcept { return this->data() != nullptr; } |
351 | | |
352 | | /** Return a view of the string. */ |
353 | 126 | string_view view() const noexcept { return this->view_; } |
354 | 0 | operator string_view() const noexcept { return this->view(); } |
355 | | |
356 | | /** Return true if the instance is an empty stringn or null string. */ |
357 | 0 | bool empty() const noexcept { return this->view_.empty(); } |
358 | | |
359 | | /** Return a pointer to the start of the string. */ |
360 | 20 | char const* data() const noexcept { return this->view_.data(); } |
361 | | |
362 | | /** Return the length of the string in bytes. */ |
363 | 6 | size_type size() const noexcept { return this->view_.size(); } |
364 | 0 | size_type length() const noexcept { return this->view_.length(); } |
365 | | |
366 | | /** Return the character at the given position. |
367 | | No bounds checking is performed. */ |
368 | 0 | char operator[](size_type pos) const noexcept { return this->view_[pos]; } |
369 | | |
370 | | /** Return the character at the given position. |
371 | | If the position is out of bounds, throws std::out_of_range. */ |
372 | 0 | char at(size_type pos) const { return this->view_.at(pos); } |
373 | | |
374 | 0 | char front() const noexcept { return this->view_.front(); } |
375 | | |
376 | 0 | char back() const noexcept { return this->view_.back(); } |
377 | | |
378 | | /** Return true if this instance is stable and otherwise false. |
379 | | An instance is stable if it is in the 'null' state or if it is |
380 | | an 'owned' state not produced by substring operations, or |
381 | | after a call to 'stabilize()' or 'str()'. */ |
382 | | bool is_stable() const; |
383 | | |
384 | | /** If 'is_stable()' does not return true, mutate so it does. */ |
385 | | void stabilize(); |
386 | | |
387 | | /** Get a pointer to a normal std::string if 'is_stable()' returns |
388 | | true and otherwise nullptr. The pointer is valid until this |
389 | | instance is mutated or destroyed. */ |
390 | | std::string const* str_if_stable() const; |
391 | | |
392 | | /** Get a reference to a normal std::string. The reference |
393 | | is valid until this instance is mutated or destroyed. */ |
394 | | std::string const& str(); |
395 | | |
396 | | /** Get a pointer to a C-style null-terminated string |
397 | | containing the same value as this instance. The pointer |
398 | | is valid until this instance is mutated, destroyed, |
399 | | or str() is called. */ |
400 | | char const* c_str(); |
401 | | |
402 | 0 | const_iterator begin() const noexcept { return this->view_.begin(); } |
403 | 0 | const_iterator end() const noexcept { return this->view_.end(); } |
404 | 0 | const_iterator cbegin() const noexcept { return this->begin(); } |
405 | 0 | const_iterator cend() const noexcept { return this->end(); } |
406 | | |
407 | | const_reverse_iterator rbegin() const noexcept |
408 | 0 | { |
409 | 0 | return this->view_.rbegin(); |
410 | 0 | } |
411 | 0 | const_reverse_iterator rend() const noexcept { return this->view_.rend(); } |
412 | 0 | const_reverse_iterator crbegin() const noexcept { return this->rbegin(); } |
413 | 0 | const_reverse_iterator crend() const noexcept { return this->rend(); } |
414 | | |
415 | | /** Append to the string using any type that implements the |
416 | | AsStringView trait. */ |
417 | | template <typename T> |
418 | | typename std::enable_if<AsStringView<T>::value, String&>::type operator+=( |
419 | | T&& s) |
420 | | { |
421 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
422 | | std::string r; |
423 | | r.reserve(this->size() + v.size()); |
424 | | r.assign(this->data(), this->size()); |
425 | | r.append(v.data(), v.size()); |
426 | | return *this = std::move(r); |
427 | | } |
428 | | template <typename T> |
429 | | typename std::enable_if<AsStringView<T>::value, String&>::type append(T&& s) |
430 | 0 | { |
431 | 0 | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
432 | 0 | std::string r; |
433 | 0 | r.reserve(this->size() + v.size()); |
434 | 0 | r.assign(this->data(), this->size()); |
435 | 0 | r.append(v.data(), v.size()); |
436 | 0 | return *this = std::move(r); |
437 | 0 | } |
438 | | |
439 | | /** Assign to an empty string. */ |
440 | 0 | void clear() { *this = ""_s; } |
441 | | |
442 | | /** Insert 'count' copies of 'ch' at position 'index'. */ |
443 | | String& insert(size_type index, size_type count, char ch); |
444 | | |
445 | | /** Insert into the string using any type that implements the |
446 | | AsStringView trait. */ |
447 | | template <typename T> |
448 | | typename std::enable_if<AsStringView<T>::value, String&>::type insert( |
449 | | size_type index, T&& s) |
450 | 0 | { |
451 | 0 | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
452 | 0 | std::string r; |
453 | 0 | r.reserve(this->size() + v.size()); |
454 | 0 | r.assign(this->data(), this->size()); |
455 | 0 | r.insert(index, v.data(), v.size()); |
456 | 0 | return *this = std::move(r); |
457 | 0 | } |
458 | | |
459 | | /** Erase 'count' characters starting at position 'index'. */ |
460 | | String& erase(size_type index = 0, size_type count = npos); |
461 | | |
462 | | void push_back(char ch) |
463 | 0 | { |
464 | 0 | std::string s; |
465 | 0 | s.reserve(this->size() + 1); |
466 | 0 | s.assign(this->data(), this->size()); |
467 | 0 | s.push_back(ch); |
468 | 0 | *this = std::move(s); |
469 | 0 | } |
470 | | |
471 | 0 | void pop_back() { *this = String(*this, 0, this->size() - 1); } |
472 | | |
473 | | template <typename T> |
474 | | typename std::enable_if<AsStringView<T>::value, String&>::type replace( |
475 | | size_type pos, size_type count, T&& s) |
476 | | { |
477 | | const_iterator first = this->begin() + pos; |
478 | | const_iterator last = first + count; |
479 | | return this->replace(first, last, std::forward<T>(s)); |
480 | | } |
481 | | |
482 | | template <typename InputIterator> |
483 | | String& replace(const_iterator first, const_iterator last, |
484 | | InputIterator first2, InputIterator last2) |
485 | | { |
486 | | std::string out; |
487 | | out.append(this->view_.begin(), first); |
488 | | out.append(first2, last2); |
489 | | out.append(last, this->view_.end()); |
490 | | return *this = std::move(out); |
491 | | } |
492 | | |
493 | | template <typename T> |
494 | | typename std::enable_if<AsStringView<T>::value, String&>::type replace( |
495 | | const_iterator first, const_iterator last, T&& s) |
496 | | { |
497 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
498 | | std::string out; |
499 | | out.reserve((first - this->view_.begin()) + v.size() + |
500 | | (this->view_.end() - last)); |
501 | | out.append(this->view_.begin(), first); |
502 | | out.append(v.data(), v.size()); |
503 | | out.append(last, this->view_.end()); |
504 | | return *this = std::move(out); |
505 | | } |
506 | | |
507 | | template <typename T> |
508 | | typename std::enable_if<AsStringView<T>::value, String&>::type replace( |
509 | | size_type pos, size_type count, T&& s, size_type pos2, |
510 | | size_type count2 = npos) |
511 | | { |
512 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
513 | | v = v.substr(pos2, count2); |
514 | | return this->replace(pos, count, v); |
515 | | } |
516 | | |
517 | | String& replace(size_type pos, size_type count, size_type count2, char ch) |
518 | 0 | { |
519 | 0 | const_iterator first = this->begin() + pos; |
520 | 0 | const_iterator last = first + count; |
521 | 0 | return this->replace(first, last, count2, ch); |
522 | 0 | } |
523 | | |
524 | | String& replace(const_iterator first, const_iterator last, size_type count2, |
525 | | char ch) |
526 | 0 | { |
527 | 0 | std::string out; |
528 | 0 | out.reserve(static_cast<size_type>(first - this->view_.begin()) + count2 + |
529 | 0 | static_cast<size_type>(this->view_.end() - last)); |
530 | 0 | out.append(this->view_.begin(), first); |
531 | 0 | out.append(count2, ch); |
532 | 0 | out.append(last, this->view_.end()); |
533 | 0 | return *this = std::move(out); |
534 | 0 | } |
535 | | |
536 | | size_type copy(char* dest, size_type count, size_type pos = 0) const; |
537 | | |
538 | 0 | void resize(size_type count) { this->resize(count, char()); } |
539 | | |
540 | | void resize(size_type count, char ch) |
541 | 0 | { |
542 | 0 | std::string s; |
543 | 0 | s.reserve(count); |
544 | 0 | if (count <= this->size()) { |
545 | 0 | s.assign(this->data(), count); |
546 | 0 | } else { |
547 | 0 | s.assign(this->data(), this->size()); |
548 | 0 | s.resize(count, ch); |
549 | 0 | } |
550 | 0 | *this = std::move(s); |
551 | 0 | } |
552 | | |
553 | | void swap(String& other) noexcept |
554 | 0 | { |
555 | 0 | std::swap(this->string_, other.string_); |
556 | 0 | std::swap(this->view_, other.view_); |
557 | 0 | } |
558 | | |
559 | | /** Return a substring starting at position 'pos' and |
560 | | consisting of at most 'count' characters. */ |
561 | | String substr(size_type pos = 0, size_type count = npos) const; |
562 | | |
563 | | template <typename T> |
564 | | typename std::enable_if<AsStringView<T>::value, int>::type compare( |
565 | | T&& s) const |
566 | | { |
567 | | return this->view_.compare(AsStringView<T>::view(std::forward<T>(s))); |
568 | | } |
569 | | |
570 | | int compare(size_type pos1, size_type count1, string_view v) const |
571 | 0 | { |
572 | 0 | return this->view_.compare(pos1, count1, v); |
573 | 0 | } |
574 | | |
575 | | int compare(size_type pos1, size_type count1, string_view v, size_type pos2, |
576 | | size_type count2) const |
577 | 0 | { |
578 | 0 | return this->view_.compare(pos1, count1, v, pos2, count2); |
579 | 0 | } |
580 | | |
581 | | int compare(size_type pos1, size_type count1, char const* s) const |
582 | 0 | { |
583 | 0 | return this->view_.compare(pos1, count1, s); |
584 | 0 | } |
585 | | |
586 | | int compare(size_type pos1, size_type count1, char const* s, |
587 | | size_type count2) const |
588 | 0 | { |
589 | 0 | return this->view_.compare(pos1, count1, s, count2); |
590 | 0 | } |
591 | | |
592 | | template <typename T> |
593 | | typename std::enable_if<AsStringView<T>::value, size_type>::type find( |
594 | | T&& s, size_type pos = 0) const |
595 | 0 | { |
596 | 0 | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
597 | 0 | return this->view_.find(v, pos); |
598 | 0 | } |
599 | | |
600 | | size_type find(char const* s, size_type pos, size_type count) const |
601 | 0 | { |
602 | 0 | return this->view_.find(s, pos, count); |
603 | 0 | } |
604 | | |
605 | | template <typename T> |
606 | | typename std::enable_if<AsStringView<T>::value, size_type>::type rfind( |
607 | | T&& s, size_type pos = npos) const |
608 | 0 | { |
609 | 0 | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
610 | 0 | return this->view_.rfind(v, pos); |
611 | 0 | } |
612 | | |
613 | | size_type rfind(char const* s, size_type pos, size_type count) const |
614 | 0 | { |
615 | 0 | return this->view_.rfind(s, pos, count); |
616 | 0 | } |
617 | | |
618 | | template <typename T> |
619 | | typename std::enable_if<AsStringView<T>::value, size_type>::type |
620 | | find_first_of(T&& s, size_type pos = 0) const |
621 | | { |
622 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
623 | | return this->view_.find_first_of(v, pos); |
624 | | } |
625 | | |
626 | | size_type find_first_of(char const* s, size_type pos, size_type count) const |
627 | 0 | { |
628 | 0 | return this->view_.find_first_of(s, pos, count); |
629 | 0 | } |
630 | | |
631 | | template <typename T> |
632 | | typename std::enable_if<AsStringView<T>::value, size_type>::type |
633 | | find_first_not_of(T&& s, size_type pos = 0) const |
634 | | { |
635 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
636 | | return this->view_.find_first_not_of(v, pos); |
637 | | } |
638 | | |
639 | | size_type find_first_not_of(char const* s, size_type pos, |
640 | | size_type count) const |
641 | 0 | { |
642 | 0 | return this->view_.find_first_not_of(s, pos, count); |
643 | 0 | } |
644 | | |
645 | | template <typename T> |
646 | | typename std::enable_if<AsStringView<T>::value, size_type>::type |
647 | | find_last_of(T&& s, size_type pos = npos) const |
648 | | { |
649 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
650 | | return this->view_.find_last_of(v, pos); |
651 | | } |
652 | | |
653 | | size_type find_last_of(char const* s, size_type pos, size_type count) const |
654 | 0 | { |
655 | 0 | return this->view_.find_last_of(s, pos, count); |
656 | 0 | } |
657 | | |
658 | | template <typename T> |
659 | | typename std::enable_if<AsStringView<T>::value, size_type>::type |
660 | | find_last_not_of(T&& s, size_type pos = npos) const |
661 | | { |
662 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
663 | | return this->view_.find_last_not_of(v, pos); |
664 | | } |
665 | | |
666 | | size_type find_last_not_of(char const* s, size_type pos, |
667 | | size_type count) const |
668 | 0 | { |
669 | 0 | return this->view_.find_last_not_of(s, pos, count); |
670 | 0 | } |
671 | | |
672 | | private: |
673 | | // Internal constructor to move from existing String. |
674 | | String(String&& s, Private) noexcept |
675 | 0 | : String(std::move(s)) |
676 | 0 | { |
677 | 0 | } |
678 | | |
679 | | // Internal constructor for dynamically allocated string. |
680 | | String(std::string&& s, Private); |
681 | | |
682 | | // Internal constructor for view of statically allocated string. |
683 | | String(string_view v, Private) |
684 | 8 | : view_(v) |
685 | 8 | { |
686 | 8 | } |
687 | | |
688 | | void internally_mutate_to_stable_string(); |
689 | | |
690 | | std::shared_ptr<std::string const> string_; |
691 | | string_view view_; |
692 | | }; |
693 | | |
694 | | /** |
695 | | * Trait for comparable types. |
696 | | */ |
697 | | template <typename T> |
698 | | struct IsComparable : std::false_type |
699 | | { |
700 | | }; |
701 | | |
702 | | template <typename T> |
703 | | struct IsComparable<T&> : IsComparable<T> |
704 | | { |
705 | | }; |
706 | | |
707 | | template <typename T> |
708 | | struct IsComparable<T const> : IsComparable<T> |
709 | | { |
710 | | }; |
711 | | |
712 | | template <typename T> |
713 | | struct IsComparable<T const*> : IsComparable<T*> |
714 | | { |
715 | | }; |
716 | | |
717 | | template <typename T, std::string::size_type N> |
718 | | struct IsComparable<T const[N]> : IsComparable<T[N]> |
719 | | { |
720 | | }; |
721 | | |
722 | | template <> |
723 | | struct IsComparable<char*> : std::true_type |
724 | | { |
725 | | }; |
726 | | |
727 | | template <std::string::size_type N> |
728 | | struct IsComparable<char[N]> : std::true_type |
729 | | { |
730 | | }; |
731 | | |
732 | | template <> |
733 | | struct IsComparable<std::string> : std::true_type |
734 | | { |
735 | | }; |
736 | | |
737 | | template <> |
738 | | struct IsComparable<char> : std::true_type |
739 | | { |
740 | | }; |
741 | | |
742 | | /** comparison operators */ |
743 | | inline bool operator==(String const& l, String const& r) |
744 | 10 | { |
745 | 10 | return l.view() == r.view(); |
746 | 10 | } |
747 | | template <typename L> |
748 | | typename std::enable_if<IsComparable<L>::value, bool>::type operator==( |
749 | | L&& l, String const& r) |
750 | | { |
751 | | return AsStringView<L>::view(std::forward<L>(l)) == r.view(); |
752 | | } |
753 | | template <typename R> |
754 | | typename std::enable_if<IsComparable<R>::value, bool>::type operator==( |
755 | | String const& l, R&& r) |
756 | | { |
757 | | return l.view() == AsStringView<R>::view(std::forward<R>(r)); |
758 | | } |
759 | | |
760 | | inline bool operator!=(String const& l, String const& r) |
761 | 0 | { |
762 | 0 | return l.view() != r.view(); |
763 | 0 | } |
764 | | template <typename L> |
765 | | typename std::enable_if<IsComparable<L>::value, bool>::type operator!=( |
766 | | L&& l, String const& r) |
767 | | { |
768 | | return AsStringView<L>::view(std::forward<L>(l)) != r.view(); |
769 | | } |
770 | | template <typename R> |
771 | | typename std::enable_if<IsComparable<R>::value, bool>::type operator!=( |
772 | | String const& l, R&& r) |
773 | | { |
774 | | return l.view() != AsStringView<R>::view(std::forward<R>(r)); |
775 | | } |
776 | | |
777 | | inline bool operator<(String const& l, String const& r) |
778 | 0 | { |
779 | 0 | return l.view() < r.view(); |
780 | 0 | } |
781 | | template <typename L> |
782 | | typename std::enable_if<IsComparable<L>::value, bool>::type operator<( |
783 | | L&& l, String const& r) |
784 | | { |
785 | | return AsStringView<L>::view(std::forward<L>(l)) < r.view(); |
786 | | } |
787 | | template <typename R> |
788 | | typename std::enable_if<IsComparable<R>::value, bool>::type operator<( |
789 | | String const& l, R&& r) |
790 | | { |
791 | | return l.view() < AsStringView<R>::view(std::forward<R>(r)); |
792 | | } |
793 | | |
794 | | inline bool operator<=(String const& l, String const& r) |
795 | 0 | { |
796 | 0 | return l.view() <= r.view(); |
797 | 0 | } |
798 | | template <typename L> |
799 | | typename std::enable_if<IsComparable<L>::value, bool>::type operator<=( |
800 | | L&& l, String const& r) |
801 | | { |
802 | | return AsStringView<L>::view(std::forward<L>(l)) <= r.view(); |
803 | | } |
804 | | template <typename R> |
805 | | typename std::enable_if<IsComparable<R>::value, bool>::type operator<=( |
806 | | String const& l, R&& r) |
807 | | { |
808 | | return l.view() <= AsStringView<R>::view(std::forward<R>(r)); |
809 | | } |
810 | | |
811 | | inline bool operator>(String const& l, String const& r) |
812 | 0 | { |
813 | 0 | return l.view() > r.view(); |
814 | 0 | } |
815 | | template <typename L> |
816 | | typename std::enable_if<IsComparable<L>::value, bool>::type operator>( |
817 | | L&& l, String const& r) |
818 | | { |
819 | | return AsStringView<L>::view(std::forward<L>(l)) > r.view(); |
820 | | } |
821 | | template <typename R> |
822 | | typename std::enable_if<IsComparable<R>::value, bool>::type operator>( |
823 | | String const& l, R&& r) |
824 | | { |
825 | | return l.view() > AsStringView<R>::view(std::forward<R>(r)); |
826 | | } |
827 | | |
828 | | inline bool operator>=(String const& l, String const& r) |
829 | 0 | { |
830 | 0 | return l.view() >= r.view(); |
831 | 0 | } |
832 | | template <typename L> |
833 | | typename std::enable_if<IsComparable<L>::value, bool>::type operator>=( |
834 | | L&& l, String const& r) |
835 | | { |
836 | | return AsStringView<L>::view(std::forward<L>(l)) >= r.view(); |
837 | | } |
838 | | template <typename R> |
839 | | typename std::enable_if<IsComparable<R>::value, bool>::type operator>=( |
840 | | String const& l, R&& r) |
841 | | { |
842 | | return l.view() >= AsStringView<R>::view(std::forward<R>(r)); |
843 | | } |
844 | | |
845 | | std::ostream& operator<<(std::ostream& os, String const& s); |
846 | | std::string& operator+=(std::string& self, String const& s); |
847 | | |
848 | | template <typename L, typename R> |
849 | | struct StringOpPlus |
850 | | { |
851 | | L l; |
852 | | R r; |
853 | | #if defined(__SUNPRO_CC) |
854 | | StringOpPlus(L in_l, R in_r) |
855 | | : l(in_l) |
856 | | , r(in_r) |
857 | | { |
858 | | } |
859 | | #endif |
860 | | operator std::string() const; |
861 | | std::string::size_type size() const |
862 | | { |
863 | | return this->l.size() + this->r.size(); |
864 | | } |
865 | | }; |
866 | | |
867 | | template <typename T> |
868 | | struct StringAdd |
869 | | { |
870 | | static bool const value = AsStringView<T>::value; |
871 | | using temp_type = string_view; |
872 | | template <typename S> |
873 | | static temp_type temp(S&& s) |
874 | | { |
875 | | return AsStringView<T>::view(std::forward<S>(s)); |
876 | | } |
877 | | }; |
878 | | |
879 | | template <typename L, typename R> |
880 | | struct StringAdd<StringOpPlus<L, R>> : std::true_type |
881 | | { |
882 | | using temp_type = StringOpPlus<L, R> const&; |
883 | | static temp_type temp(temp_type s) { return s; } |
884 | | }; |
885 | | |
886 | | template <typename L, typename R> |
887 | | StringOpPlus<L, R>::operator std::string() const |
888 | | { |
889 | | std::string s; |
890 | | s.reserve(this->size()); |
891 | | s += *this; |
892 | | return s; |
893 | | } |
894 | | |
895 | | template <typename L, typename R> |
896 | | std::string& operator+=(std::string& s, StringOpPlus<L, R> const& a) |
897 | | { |
898 | | s.reserve(s.size() + a.size()); |
899 | | s += a.l; |
900 | | s += a.r; |
901 | | return s; |
902 | | } |
903 | | |
904 | | template <typename L, typename R> |
905 | | String& operator+=(String& s, StringOpPlus<L, R> const& a) |
906 | | { |
907 | | std::string r; |
908 | | r.reserve(s.size() + a.size()); |
909 | | r.assign(s.data(), s.size()); |
910 | | r += a.l; |
911 | | r += a.r; |
912 | | s = std::move(r); |
913 | | return s; |
914 | | } |
915 | | |
916 | | template <typename L, typename R> |
917 | | std::ostream& operator<<(std::ostream& os, StringOpPlus<L, R> const& a) |
918 | | { |
919 | | return os << a.l << a.r; |
920 | | } |
921 | | |
922 | | template <typename L, typename R> |
923 | | struct IntoString<StringOpPlus<L, R>> : std::true_type |
924 | | { |
925 | | static std::string into_string(StringOpPlus<L, R> const& a) { return a; } |
926 | | }; |
927 | | |
928 | | template <typename L, typename R> |
929 | | typename std::enable_if<StringAdd<L>::value && StringAdd<R>::value, |
930 | | StringOpPlus<typename StringAdd<L>::temp_type, |
931 | | typename StringAdd<R>::temp_type>>::type |
932 | | operator+(L&& l, R&& r) |
933 | | { |
934 | | return { StringAdd<L>::temp(std::forward<L>(l)), |
935 | | StringAdd<R>::temp(std::forward<R>(r)) }; |
936 | | } |
937 | | |
938 | | template <typename LL, typename LR, typename R> |
939 | | typename std::enable_if<AsStringView<R>::value, bool>::type operator==( |
940 | | StringOpPlus<LL, LR> const& l, R&& r) |
941 | | { |
942 | | return std::string(l) == AsStringView<R>::view(std::forward<R>(r)); |
943 | | } |
944 | | |
945 | | template <typename L, typename RL, typename RR> |
946 | | typename std::enable_if<AsStringView<L>::value, bool>::type operator==( |
947 | | L&& l, StringOpPlus<RL, RR> const& r) |
948 | | { |
949 | | return AsStringView<L>::view(std::forward<L>(l)) == std::string(r); |
950 | | } |
951 | | |
952 | | } // namespace cm |
953 | | |
954 | | namespace std { |
955 | | |
956 | | template <> |
957 | | struct hash<cm::String> |
958 | | { |
959 | | using argument_type = cm::String; |
960 | | using result_type = size_t; |
961 | | |
962 | | result_type operator()(argument_type const& s) const noexcept |
963 | 106 | { |
964 | 106 | result_type const h(std::hash<cm::string_view>{}(s.view())); |
965 | 106 | return h; |
966 | 106 | } |
967 | | }; |
968 | | } |