kblib 0.2.3
General utilities library for modern C++
convert.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
32#include "kblib/stats.h"
33#if KBLIB_DEF_MACROS and not defined(pFromStr)
34# define pFromStr(type, val) ::kblib::fromStr<type>((val), #type)
35#endif
36
37#ifndef KBLIB_CONVERT_H
38# define KBLIB_CONVERT_H
39
40# include <algorithm>
41# include <array>
42# include <cassert>
43# include <chrono>
44# include <iomanip>
45# include <sstream>
46# include <stdexcept>
47# include <string>
48# include <typeinfo>
49
50# include "algorithm.h"
51# include "iterators.h"
52# include "traits.h"
53
54# if KBLIB_USE_STRING_VIEW
55
56# include <string_view>
57
63# if KBLIB_USE_SPANSTREAM
64
65# include <spanstream>
66# else
67
68# pragma GCC diagnostic push
69# pragma GCC diagnostic ignored "-W#warnings"
70# include <strstream>
71# pragma GCC diagnostic pop
72# endif
73
74# include "stringops.h"
75
76# endif
77
78# include <iostream>
79
80namespace KBLIB_NS {
81
82template <int base, typename Int>
83KBLIB_NODISCARD constexpr auto to_string(Int num) -> std::string {
84 static_assert(base <= 62 and base > 0, "Supported bases are 1 thru 62.");
85 constexpr auto digits = remove_null_terminator(
86 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
87 std::string ret;
88 bool neg = false;
89 if (num < 0) {
90 neg = true;
91 num *= -1;
92 } else if (num == 0) {
93 return "0";
94 }
95 do {
96 ret.push_back(digits[num % base]);
97 } while (num /= base);
98 if (neg) {
99 ret.push_back('-');
100 }
101 std::reverse(ret.begin(), ret.end());
102 return ret;
103}
104
105template <typename Int>
106KBLIB_NODISCARD constexpr auto to_string(Int num, int base) -> std::string {
107 assert(base <= 62 and base > 0);
108 constexpr auto digits = remove_null_terminator(
109 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
110 std::string ret;
111 bool neg = false;
112 if (num < 0) {
113 neg = true;
114 num *= -1;
115 } else if (num == 0) {
116 return "0";
117 }
118 do {
119 ret.push_back(digits[num % base]);
120 } while (num /= base);
121 if (neg) {
122 ret.push_back('-');
123 }
124 std::reverse(ret.begin(), ret.end());
125 return ret;
126}
127
131namespace detail_convert {
132
133 template <typename Result, unsigned variants, std::size_t N>
134 KBLIB_NODISCARD constexpr auto read_digits(const char* begin,
135 const char* end, unsigned base,
136 const char (&digits)[N])
137 -> Result {
138 if (begin == end) {
139 throw std::invalid_argument("\"\" is not an integer");
140 }
141 Result result{};
142 for (auto c : indirect(begin, end)) {
143 if (c != '\'') {
144 if (result > static_cast<Result>(max.of<Result>() / base)) {
145 throw std::invalid_argument("Integer out of range for type");
146 }
147 result *= base;
148 auto pos = find_in(std::begin(digits),
149 std::begin(digits) + base * variants, c);
150 if (pos != base * variants) {
151 result += pos / variants;
152 } else {
153 throw std::invalid_argument("invalid character in integer");
154 }
155 }
156 }
157 return result;
158 }
159
160} // namespace detail_convert
161
164template <typename Result>
165KBLIB_NODISCARD constexpr auto parse_integer(const char* begin, const char* end,
166 int base = 0) -> Result {
167 if (begin == end) {
168 throw std::invalid_argument("\"\" is not an integer");
169 } else if (*begin == '-') {
170 if (begin + 1 == end) {
171 throw std::invalid_argument("\"-\" is not an integer");
172 }
173 if (begin[1] == '-' or begin[1] == '+') {
174 throw std::invalid_argument("Too many signs in integer");
175 }
176 return -parse_integer<Result>(begin + 1, end, base);
177 }
178 if (begin[0] == '+') {
179 if (begin + 1 == end) {
180 throw std::invalid_argument("\"+\" is not an integer");
181 }
182 if (begin[1] == '+' or begin[1] == '-') {
183 throw std::invalid_argument("Too many signs in integer");
184 }
185 ++begin;
186 }
187 if (base == 0) {
188 if (*begin == '0') {
189 if (begin + 1 == end) {
190 return 0;
191 } else if (begin[1] == '-' or (begin + 2 != end and begin[2] == '-')) {
192 throw std::invalid_argument("unexpected - in integer");
193 } else if (begin[1] == '+' or (begin + 2 != end and begin[2] == '+')) {
194 throw std::invalid_argument("unexpected + in integer");
195 } else {
196 switch (begin[1]) {
197 case 'x':
198 return parse_integer<Result>(begin + 2, end, 16);
199 case 'b':
200 return parse_integer<Result>(begin + 2, end, 2);
201 default:
202 return parse_integer<Result>(begin + 1, end, 8);
203 }
204 }
205 } else {
206 return parse_integer<Result>(begin, end, 10);
207 }
208 } else {
209 if (base < 2 or base > 62) {
210 throw std::invalid_argument(
211 "base must be either 0 or a positive number between 2 and 62");
212 } else if (base <= 36) {
213 return detail_convert::read_digits<Result, 2>(
214 begin, end, to_unsigned(base),
215 "00112233445566778899AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRr"
216 "SsTtUuVvWwXxYyZz");
217 } else if (base <= 62) {
218 return detail_convert::read_digits<Result, 1>(
219 begin, end, to_unsigned(base),
220 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
221 }
222 }
223 // silence warning that control may flow off the end even though all paths
224 // return or throw
225 return 0;
226}
227
228template <typename Result, std::size_t N>
229KBLIB_NODISCARD constexpr auto parse_integer(const char (&in)[N], int base = 0)
230 -> Result {
231 char t = in[N - 1];
232 return parse_integer<Result>(std::begin(in), std::end(in) - +(t == '\0'),
233 base);
234}
235
236template <typename Result>
237KBLIB_NODISCARD constexpr auto parse_integer(const std::string& in,
238 int base = 0) -> Result {
239 return parse_integer<Result>(to_pointer(begin(in)), to_pointer(end(in)),
240 base);
241}
242
243# if KBLIB_USE_STRING_VIEW
244
245template <typename Result>
246KBLIB_NODISCARD constexpr auto parse_integer(std::string_view in, int base = 0)
247 -> Result {
248 return parse_integer<Result>(to_pointer(begin(in)), to_pointer(end(in)),
249 base);
250}
251
252# endif
253
254template <typename T, T V>
255struct constant : std::integral_constant<T, V> {
256 constexpr auto operator-() -> constant<T, -V> { return {}; }
257 constexpr constant() = default;
258 constexpr /* implicit */ constant(std::integral_constant<T, V>) noexcept {}
259 // reverse conversion handled by slicing
260};
261
262inline namespace literals {
263
264 template <char... Cs>
265 KBLIB_NODISCARD constexpr auto operator""_c() {
266 constexpr char arr[] = {Cs...};
268 }
269 template <char... Cs>
270 KBLIB_NODISCARD constexpr auto operator""_cu() {
271 constexpr char arr[] = {Cs...};
273 }
274
275} // namespace literals
276
277template <typename E,
278 typename = typename std::enable_if<std::is_enum<E>::value>::type>
279KBLIB_NODISCARD constexpr auto etoi(E e) -> auto {
280 return static_cast<std::underlying_type_t<E>>(e);
281}
282
283template <int maxBufLen = 4096, typename clock, typename duration>
284KBLIB_NODISCARD auto time_to_str(std::chrono::time_point<clock, duration>& tp,
285 const std::string& fmt = "%F %T")
286 -> std::string {
287 std::time_t time = clock::to_time_t(tp);
288 std::tm* tmb = std::localtime(&time);
289 std::string ret{maxBufLen, '\0'};
290 std::strftime(&ret.front(), maxBufLen, fmt.c_str(), tmb);
291 return ret;
292}
293
298namespace detail_units {
299
300 KBLIB_NODISCARD constexpr auto unit_of(std::chrono::nanoseconds) noexcept
301 -> auto {
302 return "ns";
303 }
304 KBLIB_NODISCARD constexpr auto unit_of(std::chrono::microseconds) noexcept
305 -> auto {
306 return "us";
307 }
308 KBLIB_NODISCARD constexpr auto unit_of(std::chrono::milliseconds) noexcept
309 -> auto {
310 return "ms";
311 }
312
313 KBLIB_NODISCARD constexpr auto unit_of(std::chrono::seconds) noexcept
314 -> auto {
315 return "s";
316 }
317 KBLIB_NODISCARD constexpr auto unit_of(std::chrono::minutes) noexcept
318 -> auto {
319 return "min";
320 }
321 KBLIB_NODISCARD constexpr auto unit_of(std::chrono::hours) noexcept -> auto {
322 return "hr";
323 }
324
325# if KBLIB_USE_CXX20
326
327 KBLIB_NODISCARD constexpr auto unit_of(std::chrono::days) noexcept -> auto {
328 return "ns";
329 }
330 KBLIB_NODISCARD constexpr auto unit_of(std::chrono::weeks) noexcept -> auto {
331 return "ns";
332 }
333 KBLIB_NODISCARD constexpr auto unit_of(std::chrono::months) noexcept
334 -> auto {
335 return "ns";
336 }
337 KBLIB_NODISCARD constexpr auto unit_of(std::chrono::years) noexcept -> auto {
338 return "ns";
339 }
340
341# endif
342
344 char name[16];
345 char abbr[4];
346 };
347// if std::intmax_t can represent the denominator
348# if (-1U >> 63) > (1U << 18)
349 constexpr auto name_of(std::yocto) -> prefix { return prefix{"yocto", "y"}; }
350# endif
351# if (-1U >> 63) > (1U << 8)
352 constexpr auto name_of(std::zepto) -> prefix { return prefix{"zepto", "z"}; }
353# endif
354 constexpr auto name_of(std::atto) -> prefix { return prefix{"atto", "a"}; }
355 constexpr auto name_of(std::femto) -> prefix { return prefix{"femto", "f"}; }
356 constexpr auto name_of(std::pico) -> prefix { return prefix{"pico", "p"}; }
357 constexpr auto name_of(std::nano) -> prefix { return prefix{"nano", "n"}; }
358 constexpr auto name_of(std::micro) -> prefix { return prefix{"micro", "u"}; }
359 constexpr auto name_of(std::milli) -> prefix { return prefix{"milli", "m"}; }
360 constexpr auto name_of(std::centi) -> prefix { return prefix{"centi", "c"}; }
361 constexpr auto name_of(std::deci) -> prefix { return prefix{"deci", "d"}; }
362
363 constexpr auto name_of(std::ratio<1, 1>) -> prefix { return prefix{"", ""}; }
364
365 constexpr auto name_of(std::deca) -> prefix { return prefix{"deca", "da"}; }
366 constexpr auto name_of(std::hecto) -> prefix { return prefix{"hecto", "h"}; }
367 constexpr auto name_of(std::kilo) -> prefix { return prefix{"kilo", "k"}; }
368 constexpr auto name_of(std::mega) -> prefix { return prefix{"mega", "M"}; }
369 constexpr auto name_of(std::giga) -> prefix { return prefix{"giga", "G"}; }
370 constexpr auto name_of(std::tera) -> prefix { return prefix{"tera", "T"}; }
371 constexpr auto name_of(std::peta) -> prefix { return prefix{"peta", "P"}; }
372 constexpr auto name_of(std::exa) -> prefix { return prefix{"exa", "E"}; }
373// if std::intmax_t can represent the numerator
374# if (-1U >> 63) > (1U << 8)
375 constexpr auto name_of(std::zetta) -> prefix { return prefix{"zetta", "Z"}; }
376# endif
377# if (-1U >> 63) > (1U << 18)
378 constexpr auto name_of(std::yotta) -> prefix { return prefix{"yotta", "Y"}; }
379# endif
380
381 KBLIB_NODISCARD constexpr auto largest_power_1000(std::intmax_t in) -> int {
382 if (in % 1000 == 0) {
383 return 1 + largest_power_1000(in / 1000);
384 } else {
385 return 0;
386 }
387 }
388
389 KBLIB_NODISCARD constexpr auto largest_power_1000_p(double in) -> int {
390 if (in / 1000 >= 1) {
391 return 1 + largest_power_1000_p(in / 1000.);
392 } else {
393 return 0;
394 }
395 }
396 KBLIB_NODISCARD constexpr auto largest_power_1000(double in) -> int {
397 if (in < 1) {
398 return -largest_power_1000_p(1 / in);
399 }
400 if (in / 1000 >= 1) {
401 return 1 + largest_power_1000_p(in / 1000.);
402 } else {
403 return 0;
404 }
405 }
406
407 KBLIB_NODISCARD constexpr auto pow1000(int p) -> double {
408 auto r = 1.0;
409 if (p >= 0) {
410 while (p--) {
411 r *= 1000.;
412 }
413 } else {
414 while (p++) {
415 r /= 1000.;
416 }
417 }
418 return r;
419 }
420
421 template <typename R>
422 struct is_si_ratio : std::false_type {};
423// if std::intmax_t can represent the denominator
424# if (-1U >> 63) > (1U << 18)
425 template <>
426 struct is_si_ratio<std::yocto> : std::true_type {};
427# endif
428# if (-1U >> 63) > (1U << 8)
429 template <>
430 struct is_si_ratio<std::zepto> : std::true_type {};
431# endif
432 template <>
433 struct is_si_ratio<std::atto> : std::true_type {};
434 template <>
435 struct is_si_ratio<std::femto> : std::true_type {};
436 template <>
437 struct is_si_ratio<std::pico> : std::true_type {};
438 template <>
439 struct is_si_ratio<std::nano> : std::true_type {};
440 template <>
441 struct is_si_ratio<std::micro> : std::true_type {};
442 template <>
443 struct is_si_ratio<std::milli> : std::true_type {};
444 template <>
445 struct is_si_ratio<std::centi> : std::true_type {};
446 template <>
447 struct is_si_ratio<std::deci> : std::true_type {};
448
449 template <>
450 struct is_si_ratio<std::ratio<1>> : std::true_type {};
451
452 template <>
453 struct is_si_ratio<std::deca> : std::true_type {};
454 template <>
455 struct is_si_ratio<std::hecto> : std::true_type {};
456 template <>
457 struct is_si_ratio<std::kilo> : std::true_type {};
458 template <>
459 struct is_si_ratio<std::mega> : std::true_type {};
460 template <>
461 struct is_si_ratio<std::giga> : std::true_type {};
462 template <>
463 struct is_si_ratio<std::tera> : std::true_type {};
464 template <>
465 struct is_si_ratio<std::peta> : std::true_type {};
466 template <>
467 struct is_si_ratio<std::exa> : std::true_type {};
468// if std::intmax_t can represent the numerator
469# if (-1U >> 63) > (1U << 8)
470 template <>
471 struct is_si_ratio<std::zetta> : std::true_type {};
472# endif
473# if (-1U >> 63) > (1U << 18)
474 template <>
475 struct is_si_ratio<std::yotta> : std::true_type {};
476# endif
477
478 template <typename M>
480 const char* scale_prefix;
481 char abbr[6];
483 };
484
485 template <std::intmax_t Num, std::intmax_t Den>
486 constexpr auto ratio_to_SI() noexcept -> unit_conversion<std::intmax_t> {
487 return {};
488 }
489
490 template <std::intmax_t Num, std::intmax_t Den>
491 struct nearest_ratio {};
492
493 template <std::intmax_t Num, std::intmax_t Den>
495
496} // namespace detail_units
497// TODO: duration_to_str autoscaling
498template <typename Rep, typename Ratio,
500 typename Ratio::type>::value>* = nullptr>
502 std::chrono::duration<Rep, Ratio>& d) -> std::string {
503 using ratio = typename Ratio::type;
504 auto n = detail_units::name_of(ratio{});
505 return concat(d.count() / (static_cast<double>(ratio{}.num) / ratio{}.den),
506 ' ', n.abbr, 's');
507}
508
509template <typename Rep, typename Ratio,
512KBLIB_NODISCARD constexpr auto duration_to_str(
513 std::chrono::duration<Rep, Ratio>& d) -> std::string {
514 using ratio = typename Ratio::type;
515 using n_r = detail_units::nearest_ratio_t<ratio::num, ratio::den>;
516 auto u = detail_units::name_of(n_r{});
517
518 // require an implicit cast
519 std::chrono::duration<Rep, n_r> n_d = d;
520 return concat(n_d.count(), ' ', u.abbr, 's');
521}
522
523template <typename Rep>
525 std::chrono::duration<Rep, std::ratio<60>> d) -> std::string {
526 return concat(d.count(), " min");
527}
528template <typename Rep>
530 std::chrono::duration<Rep, std::ratio<3600>> d) -> std::string {
531 return concat(d.count(), " hr");
532}
533
534template <typename string>
535KBLIB_NODISCARD auto url_encode(const string& value) -> std::string {
536 std::ostringstream escaped;
537 escaped.fill('0');
538 escaped << std::hex;
539
540 for (char c : value) {
541 // Keep alphanumeric and other accepted characters intact
542 if (std::isalnum(c) or c == '-' or c == '_' or c == '.' or c == '~') {
543 escaped << c;
544 } else {
545 // Any other characters are percent-encoded
546 escaped << std::uppercase;
547 escaped << '%' << std::setw(2) << int(to_unsigned(c));
548 escaped << std::nouppercase;
549 }
550 }
551
552 return escaped.str();
553}
554
555template <typename string>
556KBLIB_NODISCARD constexpr auto html_encode(const string& data) -> std::string {
557 std::string buffer;
558 // Arbitrary estimate for amount of growth caused by the escaping is 12.5%.
559 buffer.reserve(data.size() + data.size() / 8);
560 for (char c : data) {
561 switch (c) {
562 case '&':
563 buffer.append("&amp;");
564 break;
565 case '\"':
566 buffer.append("&quot;");
567 break;
568 case '\'':
569 buffer.append("&apos;");
570 break;
571 case '<':
572 buffer.append("&lt;");
573 break;
574 case '>':
575 buffer.append("&gt;");
576 break;
577 default:
578 buffer.push_back(c);
579 break;
580 }
581 }
582 return buffer;
583}
584
585KBLIB_NODISCARD constexpr auto escapify(char c) -> std::string {
586 auto value = to_unsigned(c);
587 if (value < ' ' or value == '\x7F' or value & to_unsigned('\x80')) {
588 constexpr std::array<char, 16> digits{
589 remove_null_terminator("0123456789ABCDEF")};
590 std::string rc("\\x ");
591 rc[2] = digits[value >> 4u];
592 rc[3] = digits[value & 15u];
593 return rc;
594 } else {
595 return std::string(1, static_cast<char>(value));
596 }
597}
598
599// Accepts any sequence of char, returns printable string
600template <typename string>
601KBLIB_NODISCARD auto escapify(const string& value) -> std::string {
602 std::ostringstream ret;
603 for (char c : value) {
604 if (c < ' ' or c >= '\x7F') {
605 ret << escapify(c);
606 } else {
607 ret << c;
608 }
609 }
610 return ret.str();
611}
612
613// Given a string and a pointer into it, calculate the effective index of that
614// pointer into a string such as created by kblib::escapify(value)
615template <typename string>
617 const char* p)
618 -> std::ptrdiff_t {
619 std::ptrdiff_t counter = 0;
620 for (auto&& c : value) {
621 if (&c == p) {
622 return counter;
623 }
624 counter += (std::isprint(c)) ? 1 : 4;
625 }
626 return counter;
627}
628
630 const char* p)
631 -> std::ptrdiff_t {
632 if (not value) {
633 throw std::invalid_argument(
634 "calculate_translated_index can't take a nullptr");
635 }
636 std::ptrdiff_t counter = 0;
637 while (*value) {
638 if (value == p) {
639 return counter;
640 }
641 counter += (std::isprint(*value)) ? 1 : 4;
642 }
643 return counter;
644}
645
646template <typename character, enable_if_t<is_character_v<character>>* = nullptr>
647KBLIB_NODISCARD constexpr auto quoted(character c) -> std::string {
648 if (c < ' ' or c >= '\x7F') {
649 return escapify(c);
650 } else if (c == '"') {
651 return "\\\"";
652 } else if (c == '\\') {
653 return "\\\\";
654 } else {
655 return {1, c};
656 }
657}
658
659template <typename string, enable_if_t<not is_character_v<string>>* = nullptr>
660KBLIB_NODISCARD auto quoted(string&& in) -> std::string {
661 std::ostringstream ret;
662 ret << '"';
663 for (char c : in) {
664 if (c < ' ' or c >= '\x7F') {
665 ret << escapify(c);
666 } else if (c == '"') {
667 ret << "\\\"";
668 } else if (c == '\\') {
669 ret << "\\\\";
670 } else {
671 ret << c;
672 }
673 }
674 ret << '"';
675 return ret.str();
676}
677
678// This only uses RTTI because C++ has no other means to get "int" from a
679// template parameter.
680template <typename T>
681KBLIB_NODISCARD auto fromStr(const std::string& val,
682 const char* type = typeid(T).name()) -> T {
683 std::stringstream ss(val);
684 T ret{};
685 if (not (ss >> std::boolalpha >> ret).fail()) {
686 return ret;
687 } else {
688 throw std::runtime_error(kblib::quoted(val) + " is not a " + type);
689 }
690}
691template <>
692KBLIB_NODISCARD constexpr auto fromStr(const std::string& val, const char*)
693 -> std::string {
694 return val;
695}
696template <>
697KBLIB_NODISCARD constexpr auto fromStr(const std::string& val, const char* type)
698 -> bool {
699 if (val == "1" or val == "true") {
700 return true;
701 } else if (val == "0" or val == "false") {
702 return false;
703 } else {
704 throw std::runtime_error(kblib::quoted(val) + " is not a " + type);
705 }
706}
707
708template <typename T>
709KBLIB_NODISCARD auto fromStr(std::string&& val,
710 const char* type = typeid(T).name()) -> T {
711 std::stringstream ss(val);
712 T ret;
713 if (not (ss >> std::boolalpha >> ret).fail()) {
714 return ret;
715 } else {
716 throw std::runtime_error(kblib::quoted(val) + " is not a " + type);
717 }
718}
719template <>
720KBLIB_NODISCARD constexpr auto fromStr(std::string&& val, const char*)
721 -> std::string {
722 return std::move(val);
723}
724template <>
725KBLIB_NODISCARD constexpr auto fromStr(std::string&& val, const char* type)
726 -> bool {
727 if (val == "1" or val == "true") {
728 return true;
729 } else if (val == "0" or val == "false") {
730 return false;
731 } else {
732 throw std::runtime_error(kblib::quoted(val) + " is not a " + type);
733 }
734}
735
736# if KBLIB_USE_STRING_VIEW
737
738template <>
739KBLIB_NODISCARD constexpr auto fromStr(const std::string& val, const char*)
740 -> std::string_view {
741 return val;
742}
743template <>
744inline auto fromStr(std::string&&, const char*) -> std::string_view = delete;
745
746template <typename T>
747KBLIB_NODISCARD auto fromStr(std::string_view val,
748 const char* type = typeid(T).name()) -> T {
749# if KBLIB_USE_SPANSTREAM
750 std::ispanstream ss(std::span<const char>(val.data(), val.size()));
751# else
752 std::istrstream ss(val.data(), kblib::to_signed(val.size()));
753# endif
754 T ret;
755 if (not (ss >> std::boolalpha >> ret).fail()) {
756 return ret;
757 } else {
758 throw std::runtime_error(kblib::quoted(val) + " is not a " + type);
759 }
760}
761template <>
762KBLIB_NODISCARD constexpr auto fromStr(std::string_view val, const char*)
763 -> std::string_view {
764 return val;
765}
766template <>
767KBLIB_NODISCARD constexpr auto fromStr(std::string_view val, const char*)
768 -> std::string {
769 return std::string(val);
770}
771template <>
772KBLIB_NODISCARD constexpr auto fromStr(std::string_view val, const char* type)
773 -> bool {
774 if (val == "1" or val == "true") {
775 return true;
776 } else if (val == "0" or val == "false") {
777 return false;
778 } else {
779 throw std::runtime_error("\"" + std::string(val) + "\" is not a " + type);
780 }
781}
782
783template <typename To, std::size_t N>
784KBLIB_NODISCARD constexpr auto fromStr(const char (&val)[N],
785 const char* type = typeid(To).name())
786 -> To {
787 // N - 1: remove null terminator
788 return fromStr<To>(std::string_view(val, N - 1), type);
789}
790
791template <typename To, typename _>
792KBLIB_NODISCARD constexpr auto fromStr(const char* val,
793 const char* type = typeid(To).name(),
794 _ = 0) -> To {
795 return fromStr<To>(std::string_view(val), type);
796}
797
798# endif
799
800template <typename T>
801KBLIB_NODISCARD auto toStr(T val) -> std::string {
802 std::stringstream ss;
803 ss << val;
804 return ss.str();
805}
806KBLIB_NODISCARD constexpr auto toStr(std::string val) -> std::string {
807 return val;
808}
809
810template <typename To, typename From>
812 static auto cast(const From& val, const char* type) -> To {
813 std::stringstream ss;
814 ss << val;
815 To ret;
816 if (not (ss >> ret).fail()) {
817 return ret;
818 } else {
819 throw std::runtime_error("Cannot convert \"" + toStr(val) + "\" to "
820 + type);
821 }
822 }
823};
824
825template <typename Same>
826struct lexical_caster<Same, Same> {
827 static constexpr auto cast(const Same& val, const char*) -> Same {
828 return val;
829 }
830};
831
832template <>
833struct lexical_caster<std::string, std::string> {
834 static constexpr auto cast(const std::string& val, const char*)
835 -> std::string {
836 return val;
837 }
838};
839
840template <typename From>
841struct lexical_caster<std::string, From> {
842 static constexpr auto cast(const From& val, const char*) -> std::string {
843 return toStr(val);
844 }
845};
846
847template <typename To>
848struct lexical_caster<To, std::string> {
849 static constexpr auto cast(const std::string& val, const char* type) -> To {
850 return fromStr<To>(val, type);
851 }
852};
853
854# if KBLIB_USE_STRING_VIEW
855
856template <>
857struct lexical_caster<std::string_view, std::string_view> {
858 static constexpr auto cast(const std::string_view& val, const char*)
859 -> std::string_view {
860 return val;
861 }
862};
863
864template <>
865struct lexical_caster<std::string_view, std::string> {
866 static constexpr auto cast(const std::string& val, const char*)
867 -> std::string_view {
868 return val;
869 }
870};
871
872template <typename From>
873struct lexical_caster<std::string_view, From> {
874 static constexpr std::enable_if_t<
875 std::is_convertible_v<From, std::string_view>, std::string_view>
876 cast(const From& val, const char*) {
877 return From(val);
878 }
879
880 // DCL50-CPP-EX2:
881 // As stated in the normative text, C-style variadic functions that are
882 // declared but never defined are permitted.
883 auto cast(...) -> std::string_view = delete;
884};
885
886template <typename To>
887struct lexical_caster<To, std::string_view> {
888 static constexpr auto cast(std::string_view val, const char* type) -> To {
889 return fromStr<To>(val, type);
890 }
891};
892
893# endif
894
895template <typename To, typename From>
896KBLIB_NODISCARD constexpr auto lexical_cast(const From& val,
897 const char* type
898 = typeid(To).name()) -> To {
899 return lexical_caster<To, From>::cast(val, type);
900}
901
902# if 0
903template <typename To, typename From>
904KBLIB_NODISCARD constexpr auto lexical_cast(const From& val,
905 const char* type = typeid(To).name()) -> To {
906 using namespace std::literals;
907 if constexpr (std::is_same_v<std::decay_t<To>, std::decay_t<From>>) {
908 return val;
909 } else if constexpr (std::is_same_v<std::decay_t<To>, std::string>) {
910 return toStr(val);
911 } else if constexpr (std::is_same_v<std::decay_t<From>, std::string>) {
912 return fromStr<To>(val, type);
913 } else {
914 std::stringstream ss;
915 ss << val;
916 To ret;
917 if (not(ss >> ret).fail())
918 return ret;
919 else
920 throw std::runtime_error("Cannot convert \""s + toStr(val) + "\" to " +
921 type);
922 }
923}
924# endif
925
926} // namespace KBLIB_NS
927
928#endif // KBLIB_CONVERT_H
Provides general-purpose algorithms, similar to the <algorithms> header.
This file provides some iterators, ranges, iterator/range adapters, and operations that can be perfor...
GeneratorWrapper< T > value(T &&value)
Definition: catch.hpp:4001
constexpr auto read_digits(const char *begin, const char *end, unsigned base, const char(&digits)[N]) -> Result
Definition: convert.h:134
constexpr auto pow1000(int p) -> double
Definition: convert.h:407
constexpr auto largest_power_1000_p(double in) -> int
Definition: convert.h:389
constexpr auto name_of(std::exa) -> prefix
Definition: convert.h:372
constexpr auto unit_of(std::chrono::hours) noexcept -> auto
Definition: convert.h:321
constexpr auto ratio_to_SI() noexcept -> unit_conversion< std::intmax_t >
Definition: convert.h:486
constexpr auto largest_power_1000(double in) -> int
Definition: convert.h:396
typename nearest_ratio< Num, Den >::type nearest_ratio_t
Definition: convert.h:494
constexpr struct kblib::nums::max_t max
constexpr auto to_signed(I x) -> std::make_signed_t< I >
Cast integral argument to corresponding signed type.
Definition: fakestd.h:609
constexpr auto to_pointer(P &&p) noexcept -> auto *
Gets a raw pointer out of any smart pointer or iterator you might pass in, without dereferencing it o...
Definition: iterators.h:72
auto time_to_str(std::chrono::time_point< clock, duration > &tp, const std::string &fmt="%F %T") -> std::string
Definition: convert.h:284
auto quoted(string &&in) -> std::string
Definition: convert.h:660
typename std::enable_if< B, T >::type enable_if_t
Definition: fakestd.h:62
constexpr auto concat(F &&f, S &&... ins) -> string
Returns a string consisting of the concatenation of all arguments.
Definition: stringops.h:312
constexpr auto e() -> T
Definition: stats.h:468
constexpr auto parse_integer(std::string_view in, int base=0) -> Result
Definition: convert.h:246
constexpr auto html_encode(const string &data) -> std::string
Definition: convert.h:556
constexpr auto quoted(character c) -> std::string
Definition: convert.h:647
constexpr auto duration_to_str(std::chrono::duration< Rep, std::ratio< 3600 > > d) -> std::string
Definition: convert.h:529
constexpr auto find_in(ForwardIt begin, EndIt end, const Elem &value) noexcept(noexcept(*begin==value)) -> size_t
Find the offset of the first ocurrence of v in a range from the beginning. It also allows for a senti...
Definition: algorithm.h:458
constexpr auto indirect(Iter1 begin, Iter2 end) noexcept(noexcept(indirect_range< Iter1, Iter2 >{begin, end})) -> indirect_range< Iter1, Iter2 >
Create a range from an iterator pair. Primarily useful for range-for loops.
Definition: iterators.h:1060
auto url_encode(const string &value) -> std::string
Definition: convert.h:535
constexpr auto fromStr(const char *val, const char *type=typeid(To).name(), _=0) -> To
Definition: convert.h:792
constexpr auto remove_null_terminator(const char(&arr)[N]) -> std::array< char, N - 1 >
Creates an array of only the meaningful characters in a string literal, and not the null terminator.
Definition: traits.h:135
constexpr auto toStr(std::string val) -> std::string
Definition: convert.h:806
constexpr auto lexical_cast(const From &val, const char *type=typeid(To).name()) -> To
Definition: convert.h:896
auto escapify(const string &value) -> std::string
Definition: convert.h:601
constexpr auto etoi(E e) -> auto
Definition: convert.h:279
constexpr auto to_unsigned(I x) -> std::make_unsigned_t< I >
Cast integral argument to corresponding unsigned type.
Definition: fakestd.h:602
constexpr auto to_string(Int num, int base) -> std::string
Definition: convert.h:106
constexpr auto calculate_translated_index(const char *value, const char *p) -> std::ptrdiff_t
Definition: convert.h:629
Definition: bits.h:719
Provides numerical and mathematical utilities.
Provides utilities for performing common operations on strings.
Definition: bits.cpp:76
constexpr constant(std::integral_constant< T, V >) noexcept
Definition: convert.h:258
constexpr constant()=default
constexpr auto operator-() -> constant< T, -V >
Definition: convert.h:256
static constexpr auto cast(const Same &val, const char *) -> Same
Definition: convert.h:827
static constexpr auto cast(const std::string &val, const char *type) -> To
Definition: convert.h:849
static constexpr auto cast(std::string_view val, const char *type) -> To
Definition: convert.h:888
static constexpr auto cast(const From &val, const char *) -> std::string
Definition: convert.h:842
static constexpr auto cast(const std::string &val, const char *) -> std::string
Definition: convert.h:834
static constexpr std::enable_if_t< std::is_convertible_v< From, std::string_view >, std::string_view > cast(const From &val, const char *)
Definition: convert.h:876
auto cast(...) -> std::string_view=delete
static constexpr auto cast(const std::string &val, const char *) -> std::string_view
Definition: convert.h:866
static constexpr auto cast(const std::string_view &val, const char *) -> std::string_view
Definition: convert.h:858
static auto cast(const From &val, const char *type) -> To
Definition: convert.h:812
static constexpr auto of() noexcept(noexcept(std::numeric_limits< T >::max()))
Definition: stats.h:203
#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.