kblib 0.2.3
General utilities library for modern C++
stringops.h
Go to the documentation of this file.
1/* *****************************************************************************
2 * kblib is a general utility library for C++14 and C++17, intended to provide
3 * performant high-level abstractions and more expressive ways to do simple
4 * things.
5 *
6 * Copyright (c) 2021 killerbee
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 * ****************************************************************************/
21
31#ifndef KBLIB_STRINGOPS_H
32#define KBLIB_STRINGOPS_H
33
34#include "algorithm.h"
35#include "format.h"
36#include "tdecl.h"
37#include "traits.h"
38
39#include <algorithm>
40#include <cctype>
41#include <cwctype>
42#include <initializer_list>
43#include <numeric>
44#include <string>
45#include <type_traits>
46
47#if KBLIB_USE_CXX17
48# include <string_view>
49#endif
50
51namespace KBLIB_NS {
52
53#if true or KBLIB_USE_CXX17
61template <typename C>
63 : contains_type<std::tuple<char, wchar_t, char16_t, char32_t
64# if __cpp_char8_t
65 ,
66 char8_t
67# endif
68 >,
69 std::decay_t<C>> {
70};
71
75template <typename C>
77
78namespace detail {
79
88 using type = void;
89 };
96 template <typename T>
97 struct arithmetic_type<T, true> {
98 using type = T;
99 };
103 template <typename T>
105
118 template <typename T, typename = arithmetic_type_t<T>>
119 struct str_type {
124 using type = std::string;
130 KBLIB_NODISCARD static auto convert(T in) -> std::string {
131 return std::to_string(in);
132 }
133 };
145 template <typename T>
146 struct str_type<T, void> {
152 using type = std::conditional_t<std::is_convertible_v<T, std::string>,
153 std::string, T>;
157 KBLIB_NODISCARD static auto convert(T&& in) -> type {
158 return std::forward<T>(in);
159 }
160 };
164 template <>
165 struct str_type<char, char> {
166 using type = char;
167 KBLIB_NODISCARD static auto convert(char in) -> char { return in; }
168 };
172 template <>
173 struct str_type<wchar_t, wchar_t> {
174 using type = wchar_t;
175 KBLIB_NODISCARD static auto convert(wchar_t in) -> wchar_t { return in; }
176 };
180 template <>
181 struct str_type<char16_t, char16_t> {
182 using type = char16_t;
183 KBLIB_NODISCARD static auto convert(char16_t in) -> char16_t {
184 return in;
185 }
186 };
190 template <>
191 struct str_type<char32_t, char32_t> {
192 using type = char32_t;
193 KBLIB_NODISCARD static auto convert(char32_t in) -> char32_t {
194 return in;
195 }
196 };
197# if __cpp_char8_t
201 template <>
202 struct str_type<char8_t, char8_t> {
203 using type = char8_t;
204 KBLIB_NODISCARD static auto convert(char8_t in) -> char8_t { return in; }
205 };
206# endif
210 template <typename T>
212
213} // namespace detail
214
215# if KBLIB_USE_CXX17
223template <typename Str>
224KBLIB_NODISCARD constexpr auto strsize(Str&& str) -> std::size_t {
225 if constexpr (std::is_array_v<std::remove_reference_t<Str>>) {
226 return fakestd::size(str);
227 } else if constexpr (std::is_pointer_v<std::decay_t<Str>>) {
228 return std::char_traits<std::decay_t<decltype(*str)>>::length(str);
229 } else if constexpr (is_character_v<std::decay_t<Str>>) {
230 return 1;
231 } else if constexpr (std::is_integral_v<std::decay_t<Str>>) {
232 return to_unsigned(count_digits(str));
233 } else if constexpr (std::is_floating_point_v<std::decay_t<Str>>) {
234 return to_unsigned(count_digits(str));
235 } else {
236 return fakestd::size(str);
237 }
238}
239
240template <typename CharT>
241KBLIB_NODISCARD constexpr auto length(const CharT* str) noexcept
242 -> std::size_t {
244}
245
257template <typename string, typename F, typename... S>
258constexpr auto append(string&& out, F&& f, S&&... tail) -> void {
259 if constexpr (is_character_v<std::decay_t<F>>) {
260 out.append(1, f);
261 } else if constexpr (std::is_arithmetic_v<std::decay_t<F>>) {
262 out.append(std::to_string(f));
263 } else {
264 out.append(f);
265 }
266 if constexpr (sizeof...(S) > 0) {
267 append(out, tail...);
268 }
269 return;
270}
271
272namespace detail {
273
274 template <std::size_t I, typename T>
275 struct value {
276 T v;
277 };
278
279 template <class Idxs, class... Ts>
280 struct values;
281
282 template <std::size_t... Idxs, typename... Ts>
283 struct values<std::index_sequence<Idxs...>, Ts...> : value<Idxs, Ts>... {};
284
285 template <typename string, typename... S, std::size_t... I>
286 KBLIB_NODISCARD constexpr auto concat_impl(std::index_sequence<I...>,
287 S&&... ins) -> string {
288 values<std::index_sequence<I...>, detail::str_type_t<S>...> buf{
289 {detail::str_type<S>::convert(std::forward<S>(ins))}...};
290 string ret;
291 std::size_t size
292 = (strsize(static_cast<value<I, detail::str_type_t<S>>&>(buf).v) + ...
293 + 0);
294 ret.reserve(size);
295 append(ret, static_cast<value<I, detail::str_type_t<S>>&>(buf).v...);
296 return ret;
297 }
298
299} // namespace detail
300
311template <typename string = std::string, typename F, typename... S>
312KBLIB_NODISCARD constexpr auto concat(F&& f, S&&... ins) -> string {
313 return detail::concat_impl<string>(
314 std::make_index_sequence<1 + sizeof...(S)>{}, std::forward<F>(f),
315 std::forward<S>(ins)...);
316}
317
325template <typename string = std::string, typename str>
326KBLIB_NODISCARD constexpr auto concat(std::initializer_list<str> ins)
327 -> string {
328 string ret;
329 ret.reserve(std::accumulate(
330 ins.begin(), ins.end(), std::size_t{0},
331 [](std::size_t z, const str& s) { return z + strsize(s); }));
332 for (auto&& s : ins) {
333 append(ret, s);
334 }
335 return ret;
336}
337# endif
338
339KBLIB_NODISCARD constexpr auto isspace(char c) -> bool {
340 return std::isspace(to_unsigned(c));
341}
342KBLIB_NODISCARD constexpr auto isspace(wchar_t c) -> bool {
343 return iswspace(to_unsigned(c));
344}
345
346struct is_space {
347 KBLIB_NODISCARD auto operator()(char c) -> bool { return isspace(c); }
348 KBLIB_NODISCARD auto operator()(wchar_t c) -> bool { return isspace(c); }
349};
350
351KBLIB_NODISCARD constexpr auto isAspace(char c) -> bool {
352 for (auto v : " \t\r\n\f\v") {
353 if (c == v) {
354 return true;
355 }
356 }
357 return false;
358}
359KBLIB_NODISCARD constexpr auto isAspace(wchar_t c) -> bool {
360 for (auto v : L" \t\r\n\f\v") {
361 if (c == v) {
362 return true;
363 }
364 }
365 return false;
366}
367
377template <typename range, typename string = std::string>
378KBLIB_NODISCARD constexpr auto join(const range& in,
379 const string& joiner = "") {
380 if (fakestd::size(in) > 0) {
381
382 auto len = kblib::accumulate(
383 begin(in), end(in), std::size_t{},
384 [](std::size_t l, const auto& x) { return l + strsize(x); });
385 auto ret = *begin(in);
386 try_reserve(ret, len);
387 kblib::copy(next(begin(in)), end(in),
388 consumer([&](const auto& x) { append(ret, joiner, x); }));
389 return ret;
390 } else {
391 return typename value_type_linear<range>::type{};
392 }
393}
394#endif // KBLIB_USE_CXX17
395
404template <typename Container = std::vector<std::string>, typename Predicate,
405 typename String>
406KBLIB_NODISCARD constexpr auto split_tokens(const String& in, Predicate spacer)
409 typename Container::value_type::value_type>::value,
410 Container> {
411 Container ret{};
412 bool delim_run = true;
413 const char* begpos{};
414 auto endpos = begpos;
415 for (const auto& c : in) {
416 if (delim_run) {
417 // keep begpos updated as long as in a delimiter run
418 begpos = &c;
419 }
420 if (spacer(c) and not std::exchange(delim_run, true)) {
421 // c is first of a run of delimiters
422 ret.emplace_back(begpos, &c - begpos);
423 } else if (not spacer(c)) {
424 // c is not a delimiter
425 delim_run = false;
426 }
427 endpos = &c;
428 }
429 if (not std::exchange(delim_run, true)) {
430 ret.emplace_back(begpos, endpos + 1);
431 }
432 if (not delim_run and begpos != endpos) {
433 ret.emplace_back(begpos, endpos - begpos + 1);
434 }
435 return ret;
436}
437
444template <typename Container = std::vector<std::string>, typename String>
445KBLIB_NODISCARD constexpr auto split_tokens(const String& in) -> Container {
446 return split_tokens(in, is_space{});
447}
448
456template <typename Container = std::vector<std::string>, typename String>
458 const String& in,
459 typename Container::value_type::value_type delim) -> Container {
460 Container ret{};
461 bool delim_run = true;
462 using CharT = typename Container::value_type::value_type;
463 const CharT* begpos{};
464 auto endpos = begpos;
465 for (const CharT& c : in) {
466 if (delim_run) {
467 // keep begpos updated as long as in a delimiter run
468 begpos = &c;
469 }
470 if (c == delim and not std::exchange(delim_run, true)) {
471 // c is first of a run of delimiters
472 ret.emplace_back(begpos, &c - begpos);
473 } else if (c != delim) {
474 // c is not a delimiter
475 delim_run = false;
476 }
477 endpos = &c;
478 }
479 if (not delim_run and begpos != endpos) {
480 ret.emplace_back(&*begpos, endpos - begpos + 1);
481 }
482 return ret;
483}
484
485template <typename Container = std::vector<std::string>, typename String>
486KBLIB_NODISCARD constexpr auto kbsplit2(const String& in,
487 char delim = ' ') -> Container {
488 Container ret{""};
489 bool delim_run = true;
490 for (char c : in) {
491 if (c == delim and not std::exchange(delim_run, true)) {
492 // c is first of a run of delimiters
493 ret.emplace_back();
494 } else if (c != delim) {
495 // c is not a delimiter
496 delim_run = false;
497 ret.back().push_back(c);
498 }
499 }
500 if (ret.back().empty()) {
501 ret.pop_back();
502 }
503 return ret;
504}
505
513template <typename Container = std::vector<std::string>, typename String>
514KBLIB_NODISCARD constexpr auto split_dsv(const String& str,
515 char delim) -> Container {
516 Container ret;
517 for (std::size_t pos1{}, pos2{str.find(delim)}; pos1 != str.npos;) {
518 ret.emplace_back(str, pos1, pos2 - pos1);
519 pos1 = std::exchange(pos2, str.find(delim, pos2 + 1));
520 if (pos1 != str.npos) {
521 ++pos1;
522 }
523 }
524 return ret;
525}
526
534template <typename Container = std::vector<std::string>, typename String,
535 typename Predicate>
536KBLIB_NODISCARD constexpr auto split_dsv(const String& str, Predicate delim)
539 typename Container::value_type::value_type>::value,
540 Container> {
541 Container ret;
542 for (std::size_t pos1{}, pos2{str.find(delim)}; pos1 != str.npos;) {
543 ret.emplace_back(str, pos1, pos2 - pos1);
544 pos1 = std::exchange(
545 pos2, kblib::find_in_if(str.begin() + pos1 + 1, str.end(), delim));
546 if (pos1 != str.npos) {
547 ++pos1;
548 }
549 }
550 return ret;
551}
552
553// TODO(killerbee13): figure out if any uses of reverseStr, toLower, toUpper
554// exist in current projects
555
565template <typename string>
566KBLIB_NODISCARD constexpr auto reverse_str(string val) -> string {
567 std::reverse(val.begin(), val.end());
568 return val;
569}
570
571namespace detail {
572
573 template <typename CharT>
574 KBLIB_NODISCARD constexpr auto to_int_type(CharT ch) {
576 }
577 template <typename CharT, typename IntT>
578 KBLIB_NODISCARD constexpr auto to_char_type(IntT ch) {
580 }
581
582 KBLIB_NODISCARD constexpr auto tolower(char ch) {
583 return to_char_type<char>(std::tolower(to_int_type(ch)));
584 }
585
586 KBLIB_NODISCARD constexpr auto towlower(wchar_t ch) {
587 return to_char_type<wchar_t>(std::towlower(to_int_type(ch)));
588 }
589
590 KBLIB_NODISCARD constexpr auto toupper(char ch) {
591 return to_char_type<char>(std::toupper(to_int_type(ch)));
592 }
593
594 KBLIB_NODISCARD constexpr auto towupper(wchar_t ch) {
595 return to_char_type<wchar_t>(std::towupper(to_int_type(ch)));
596 }
597} // namespace detail
604template <typename string>
605KBLIB_NODISCARD constexpr auto tolower(string str) -> string {
606 std::transform(str.begin(), str.end(), str.begin(),
607 [](auto c) { return detail::tolower(c); });
608 return str;
609}
610
617template <typename string>
618KBLIB_NODISCARD constexpr auto toupper(string str) -> string {
619 std::transform(str.begin(), str.end(), str.begin(),
620 [](auto c) { return detail::toupper(c); });
621 return str;
622}
623
635template <typename string>
636KBLIB_NODISCARD constexpr auto repeat(string val, std::size_t count) -> string {
637 string tmp;
638 try_reserve(tmp, fakestd::size(val) * count);
639 for (std::size_t i = 0; i < count; ++i) {
640 tmp += val;
641 }
642 return tmp;
643}
653KBLIB_NODISCARD constexpr auto repeat(char val,
654 std::size_t count) -> std::string {
655 return std::string(count, val);
656}
657
658#if KBLIB_USE_STRING_VIEW
659
666KBLIB_NODISCARD constexpr auto ends_with(std::string_view haystack,
667 std::string_view needle) -> bool {
668 return haystack.size() >= needle.size()
669 and haystack.compare(haystack.size() - needle.size(),
670 std::string_view::npos, needle)
671 == 0;
672}
673
680KBLIB_NODISCARD constexpr auto ends_with(std::string_view haystack,
681 char needle) -> bool {
682 return not haystack.empty() and haystack.back() == needle;
683}
684
691KBLIB_NODISCARD constexpr auto starts_with(std::string_view haystack,
692 std::string_view needle) -> bool {
693 return haystack.size() >= needle.size()
694 and haystack.compare(0, needle.size(), needle) == 0;
695}
696
703KBLIB_NODISCARD constexpr auto starts_with(std::string_view haystack,
704 char needle) -> bool {
705 return not haystack.empty() and haystack.front() == needle;
706}
707
708#endif
709
710} // namespace KBLIB_NS
711
712#endif // KBLIB_STRINGOPS_H
Provides general-purpose algorithms, similar to the <algorithms> header.
Contains some utilities for manipulating and querying string representations.
GeneratorWrapper< T > value(T &&value)
Definition: catch.hpp:4001
Generic::PredicateMatcher< T > Predicate(std::function< bool(T const &)> const &predicate, std::string const &description="")
Definition: catch.hpp:3521
constexpr auto concat_impl(std::index_sequence< I... >, S &&... ins) -> string
Definition: stringops.h:286
typename str_type< T >::type str_type_t
Provides the natural stringlike type for representing a T.
Definition: stringops.h:211
typename arithmetic_type< T >::type arithmetic_type_t
Equivalent to typename arithmetic_type<T>::type.
Definition: stringops.h:104
constexpr auto to_int_type(CharT ch)
Definition: stringops.h:574
constexpr auto to_char_type(IntT ch)
Definition: stringops.h:578
constexpr auto towlower(wchar_t ch)
Definition: stringops.h:586
constexpr auto towupper(wchar_t ch)
Definition: stringops.h:594
constexpr auto size(const C &c) -> decltype(c.size())
Definition: fakestd.h:383
constexpr auto exchange(T &obj, U &&new_value) -> T
Definition: fakestd.h:738
auto consumer(F f) -> consume_iterator< F >
Creates a consume_iterator of deduced type F.
Definition: iterators.h:1614
constexpr auto isAspace(wchar_t c) -> bool
Definition: stringops.h:359
constexpr auto repeat(char val, std::size_t count) -> std::string
Construct a string consisting of count copies of val.
Definition: stringops.h:653
constexpr auto concat(std::initializer_list< str > ins) -> string
Returns a string consisting of the concatenation of all elements of an initializer list.
Definition: stringops.h:326
constexpr auto starts_with(std::string_view haystack, char needle) -> bool
Checks if a given string starts with a particular string.
Definition: stringops.h:703
constexpr auto toupper(string str) -> string
Folds all characters in a string using the default execution character set to uppercase.
Definition: stringops.h:618
constexpr auto tolower(string str) -> string
Folds all characters in a string using the default execution character set to lowercase.
Definition: stringops.h:605
constexpr auto append(string &&out, F &&f, S &&... tail) -> void
Given an object out of resizable stringlike type string, appends all other arguments to it.
Definition: stringops.h:258
constexpr auto kbsplit2(const String &in, char delim=' ') -> Container
Definition: stringops.h:486
constexpr auto isspace(wchar_t c) -> bool
Definition: stringops.h:342
auto try_reserve(C &c, std::size_t s) noexcept(noexcept(c.reserve(s))) -> void
Attempt to reserve capacity in a container. No-op if unsupported.
Definition: traits.h:239
constexpr bool is_character_v
Equivalent to is_character<C>::value.
Definition: stringops.h:76
constexpr auto range(Value min, Value max, Delta step=0) -> range_t< Value, Delta >
Constructs a range from beginning, end, and step amount. The range is half-open, that is min is in th...
Definition: iterators.h:621
constexpr auto length(const CharT *str) noexcept -> std::size_t
Definition: stringops.h:241
constexpr auto to_string(Int num) -> std::string
Definition: convert.h:83
typename std::decay< T >::type decay_t
Definition: fakestd.h:65
constexpr auto reverse_str(string val) -> string
Reverses all the elements of its input.
Definition: stringops.h:566
constexpr auto count_digits(Number val) -> enable_if_t< std::is_floating_point< Number >::value, int >
Calculates the number of decimal digits needed to represent a number, plus one for negative numbers.
Definition: format.h:50
constexpr auto accumulate(InputIt first, InputIt last, T init) -> T
A constexpr version of std::accumulate.
Definition: algorithm.h:162
constexpr auto copy(InputIt first, EndIt last, OutputIt out) -> OutputIt
Copies all elements of [first, last) to out. It also allows for a sentinel end iterator.
Definition: algorithm.h:1322
constexpr auto split_tokens(const String &in, typename Container::value_type::value_type delim) -> Container
Split a string on all instances of a delimiter.
Definition: stringops.h:457
typename return_assert< V, T >::type return_assert_t
Definition: fakestd.h:560
constexpr auto ends_with(std::string_view haystack, char needle) -> bool
Checks if a given string ends with a particular string.
Definition: stringops.h:680
constexpr auto split_dsv(const String &str, Predicate delim) -> return_assert_t< is_callable< Predicate, typename Container::value_type::value_type >::value, Container >
Split a string on all instances of delim.
Definition: stringops.h:536
constexpr auto join(const range &in, const string &joiner="")
Concatenates all elements of a range together with an optional joiner.
Definition: stringops.h:378
constexpr auto find_in_if(ForwardIt begin, EndIt end, UnaryPredicate pred) noexcept(noexcept(kblib::invoke(pred, *begin))) -> size_t
Find the offset of the first element for which p returns true. It also allows for a sentinel end iter...
Definition: algorithm.h:474
constexpr auto to_unsigned(I x) -> std::make_unsigned_t< I >
Cast integral argument to corresponding unsigned type.
Definition: fakestd.h:602
constexpr auto strsize(Str &&str) -> std::size_t
Determines the size in characters of any valid argument to concat or append.
Definition: stringops.h:224
constexpr auto transform(InputIt first, EndIt last, OutputIt d_first, UnaryOperation unary_op) -> OutputIt
transform applies the given function to a range and stores the result in another range,...
Definition: algorithm.h:1632
Definition: bits.h:719
Determines if T is a type in Tuple, which must be a std::tuple.
Definition: traits.h:52
Filter only arithmetic types.
Definition: stringops.h:87
static auto convert(T &&in) -> type
Returns the potentially-converted argument.
Definition: stringops.h:157
std::conditional_t< std::is_convertible_v< T, std::string >, std::string, T > type
Non-arithmetic types are either already stringlike, are convertible to std::string,...
Definition: stringops.h:153
static auto convert(char16_t in) -> char16_t
Definition: stringops.h:183
static auto convert(char32_t in) -> char32_t
Definition: stringops.h:193
static auto convert(char in) -> char
Definition: stringops.h:167
static auto convert(wchar_t in) -> wchar_t
Definition: stringops.h:175
Converts types to strings.
Definition: stringops.h:119
std::string type
Arithmetic types can be converted into strings using the standard library.
Definition: stringops.h:124
static auto convert(T in) -> std::string
Forwards to std::to_string.
Definition: stringops.h:130
Determine if the given type, ignoring const or reference qualifiers, is a character type.
Definition: stringops.h:69
auto operator()(wchar_t c) -> bool
Definition: stringops.h:348
auto operator()(char c) -> bool
Definition: stringops.h:347
Provides macros and basic templates used by the rest of kblib.
#define KBLIB_NS
Definition: tdecl.h:130
#define KBLIB_NODISCARD
This internal macro is used to provide a fallback for [[nodiscard]] in C++14.
Definition: tdecl.h:146
Contains some type traits not in the standard library that are useful in the implementation of kblib.