#if KBLIB_DEF_MACROS and not defined(pFromStr) #define pFromStr(type, val) ::kblib::fromStr((val), #type) #endif #ifndef KBLIB_CONVERT_H #define KBLIB_CONVERT_H #include #include #include #include #include #include #include #include #include #include "traits.h" #if KBLIB_USE_STRING_VIEW #include #include #include "stringops.h" #endif #include namespace kblib { template inline std::string to_string(Int num) { static_assert(base <= 62 and base > 0, "Supported bases are 1 thru 62."); constexpr auto digits = remove_null_terminator( "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); std::string ret; bool neg = false; if (num < 0) { neg = true; num *= -1; } else if (num == 0) { return "0"; } do { ret.push_back(digits[num % base]); } while (num /= base); if (neg) { ret.push_back('-'); } std::reverse(ret.begin(), ret.end()); return ret; } template inline std::string to_string(Int num, int base) { assert(base <= 62 and base > 0); constexpr auto digits = remove_null_terminator( "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); std::string ret; bool neg = false; if (num < 0) { neg = true; num *= -1; } else if (num == 0) { return "0"; } do { ret.push_back(digits[num % base]); } while (num /= base); if (neg) { ret.push_back('-'); } std::reverse(ret.begin(), ret.end()); return ret; } namespace detail { template constexpr Result read_digits(const char* begin, const char* end, int base, const char (&digits)[N]) { if (begin == end) { throw std::invalid_argument("\"\" is not an integer"); } Result result{}; for (auto c : indirect(begin, end)) { if (c != '\'') { result *= base; if (auto pos = find_in(std::begin(digits), std::begin(digits) + base * variants, c); pos != std::size(digits)) { result += pos / variants; } else { throw std::invalid_argument("invalid character in integer"); } } } return result; } } // namespace detail template constexpr Result parse_integer(const char* begin, const char* end, int base = 0) { if (base == 0) { if (*begin == '0') { if (begin + 1 == end) { return 0; } else { switch (begin[1]) { case 'x': return parse_integer(begin + 2, end, 16); case 'b': return parse_integer(begin + 2, end, 2); default: return parse_integer(begin + 1, end, 8); } } } else { return parse_integer(begin, end, 10); } } else { if (base < 2 or base > 62) { throw std::invalid_argument( "base must be either 0 or a positive number between 2 and 62"); } else if (base <= 36) { return detail::read_digits( begin, end, base, "00112233445566778899AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRr" "SsTtUuVvWwXxYyZz"); } else if (base <= 62) { return detail::read_digits( begin, end, base, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); } } throw "unreachable"; } template constexpr Result parse_integer(const char (&in)[N], int base = 0) { return parse_integer(std::begin(in), std::end(in), base); } template constexpr Result parse_integer(const std::string& in, int base = 0) { return parse_integer(begin(in), end(in), base); } #if KBLIB_USE_STRING_VIEW template constexpr Result parse_integer(std::string_view in, int base = 0) { return parse_integer(begin(in), end(in), base); } #endif template struct constant : std::integral_constant { constexpr constant<-V> operator-() { return {}; } }; inline namespace literals { template constexpr auto operator""_c() { constexpr char arr[] = {Cs...}; return constant(arr)>{}; } template constexpr auto operator""_cu() { constexpr char arr[] = {Cs...}; return constant(arr)>{}; } } // namespace literals template ::value>::type> constexpr auto etoi(E e) { return static_cast>(e); } template std::string time_to_str(std::chrono::time_point& tp, const std::string& fmt = "%F %T") { std::time_t time = clock::to_time_t(tp); std::tm* tmb = std::localtime(&time); std::string ret{maxBufLen, '\0'}; std::strftime(&ret.front(), maxBufLen, fmt.c_str(), tmb); return ret; } namespace detail { constexpr auto unit_of(std::chrono::nanoseconds) noexcept { return "ns"; } constexpr auto unit_of(std::chrono::microseconds) noexcept { return "us"; } constexpr auto unit_of(std::chrono::milliseconds) noexcept { return "ms"; } constexpr auto unit_of(std::chrono::seconds) noexcept { return "s"; } constexpr auto unit_of(std::chrono::minutes) noexcept { return "min"; } constexpr auto unit_of(std::chrono::hours) noexcept { return "hr"; } #if KBLIB_USE_CXX20 constexpr auto unit_of(std::chrono::days) noexcept { return "ns"; } constexpr auto unit_of(std::chrono::weeks) noexcept { return "ns"; } constexpr auto unit_of(std::chrono::months) noexcept { return "ns"; } constexpr auto unit_of(std::chrono::years) noexcept { return "ns"; } #endif struct prefix { char name[16]; char abbr[4]; }; // if std::intmax_t can represent the denominator #if (-1U >> 63) > (1U << 18) constexpr auto name_of(std::yocto) -> prefix { return {"yocto", "y"}; } #endif #if (-1U >> 63) > (1U << 8) constexpr auto name_of(std::zepto) -> prefix { return {"zepto", "z"}; } #endif constexpr auto name_of(std::atto) -> prefix { return {"atto", "a"}; } constexpr auto name_of(std::femto) -> prefix { return {"femto", "f"}; } constexpr auto name_of(std::pico) -> prefix { return {"pico", "p"}; } constexpr auto name_of(std::nano) -> prefix { return {"nano", "n"}; } constexpr auto name_of(std::micro) -> prefix { return {"micro", "u"}; } constexpr auto name_of(std::milli) -> prefix { return {"milli", "m"}; } constexpr auto name_of(std::centi) -> prefix { return {"centi", "c"}; } constexpr auto name_of(std::deci) -> prefix { return {"deci", "d"}; } constexpr auto name_of(std::ratio<1, 1>) -> prefix { return {"", ""}; } constexpr auto name_of(std::deca) -> prefix { return {"deca", "da"}; } constexpr auto name_of(std::hecto) -> prefix { return {"hecto", "h"}; } constexpr auto name_of(std::kilo) -> prefix { return {"kilo", "k"}; } constexpr auto name_of(std::mega) -> prefix { return {"mega", "M"}; } constexpr auto name_of(std::giga) -> prefix { return {"giga", "G"}; } constexpr auto name_of(std::tera) -> prefix { return {"tera", "T"}; } constexpr auto name_of(std::peta) -> prefix { return {"peta", "P"}; } constexpr auto name_of(std::exa) -> prefix { return {"exa", "E"}; } // if std::intmax_t can represent the numerator #if (-1U >> 63) > (1U << 8) constexpr auto name_of(std::zetta) -> prefix { return {"zetta", "Z"}; } #endif #if (-1U >> 63) > (1U << 18) constexpr auto name_of(std::yotta) -> prefix { return {"yotta", "Y"}; } #endif constexpr int largest_power_1000(std::intmax_t in) { if (in % 1000 == 0) { return 1 + largest_power_1000(in / 1000); } else { return 0; } } constexpr int largest_power_1000_p(double in) { if (in / 1000 >= 1) { return 1 + largest_power_1000_p(in / 1000.); } else { return 0; } } constexpr int largest_power_1000(double in) { if (in < 1) { return -largest_power_1000_p(1 / in); } if (in / 1000 >= 1) { return 1 + largest_power_1000_p(in / 1000.); } else { return 0; } } constexpr double pow1000(int p) { auto r = 1.0; if (p >= 0) { while (p--) { r *= 1000.; } } else { while (p++) { r /= 1000.; } } return r; } template struct is_si_ratio : std::false_type {}; // if std::intmax_t can represent the denominator #if (-1U >> 63) > (1U << 18) template<> struct is_si_ratio : std::true_type {}; #endif #if (-1U >> 63) > (1U << 8) template<> struct is_si_ratio : std::true_type {}; #endif template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio> : std::true_type{}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; template<> struct is_si_ratio : std::true_type {}; // if std::intmax_t can represent the numerator #if (-1U >> 63) > (1U << 8) template<> struct is_si_ratio : std::true_type {}; #endif #if (-1U >> 63) > (1U << 18) template<> struct is_si_ratio : std::true_type {}; #endif template struct unit_conversion { const char* scale_prefix; char abbr[6]; M multiplier; }; template auto ratio_to_SI() noexcept -> unit_conversion {} template struct nearest_ratio { }; template using nearest_ratio_t = typename nearest_ratio::type; } // namespace detail template ::value>* = 0> std::string duration_to_str(std::chrono::duration& d) { using ratio = typename Ratio::type; auto cv = detail::ratio_to_SI(); return concat(d.count() * cv.multiplier, ' ', cv.abbr, 's'); } template ::value>* = 0> std::string duration_to_str(std::chrono::duration& d) { using ratio = typename Ratio::type; using n_r = detail::nearest_ratio_t; auto u = detail::name_of(n_r{}); // require an implicit cast std::chrono::duration n_d = d; return concat(n_d.count(), ' ', u.abbr, 's'); } template inline std::string duration_to_str(std::chrono::duration> d) { return concat(d.count(), " min"); } template inline std::string duration_to_str(std::chrono::duration> d) { return concat(d.count(), " hr"); } template inline std::string url_encode(const string& value) { std::ostringstream escaped; escaped.fill('0'); escaped << std::hex; for (char c : value) { // Keep alphanumeric and other accepted characters intact if (std::isalnum(c) or c == '-' or c == '_' or c == '.' or c == '~') { escaped << c; } else { // Any other characters are percent-encoded escaped << std::uppercase; escaped << '%' << std::setw(2) << int(to_unsigned(c)); escaped << std::nouppercase; } } return escaped.str(); } template inline std::string html_encode(const string& data) { std::string buffer; // Arbitrary estimate for amount of growth caused by the escaping is 12.5%. buffer.reserve(data.size() + data.size() / 8); for (char c : data) { switch (c) { case '&': buffer.append("&"); break; case '\"': buffer.append("""); break; case '\'': buffer.append("'"); break; case '<': buffer.append("<"); break; case '>': buffer.append(">"); break; default: buffer.push_back(c); break; } } return buffer; } inline std::string escapify(char c) { auto value = to_unsigned(c); if (value < ' ' or value == '\x7F' or value & to_unsigned('\x80')) { constexpr std::array digits{ remove_null_terminator("0123456789ABCDEF")}; std::string rc("\\x "); rc[2] = digits[value >> 4u]; rc[3] = digits[value & 15u]; return rc; } else { return std::string(1, static_cast(value)); } } // Accepts any sequence of char, returns printable string template std::string escapify(const string& value) { std::ostringstream ret; for (char c : value) { if (c < ' ' or c >= '\x7F') { ret << escapify(c); } else { ret << c; } } return ret.str(); } // Given a string and a pointer into it, calculate the effective index of that // pointer into a string such as created by kblib::escapify(value) template int calculate_translated_index(string&& value, const char* p) { int counter = 0; for (auto&& c : value) { if (&c == p) { return counter; } counter += (std::isprint(c)) ? 1 : 4; } return counter; } // template <> inline int calculate_translated_index(const char* value, const char* p) { if (not value) { throw std::invalid_argument( "calculate_translated_index can't take a nullptr"); } int counter = 0; while (*value) { if (value == p) { return counter; } counter += (std::isprint(*value)) ? 1 : 4; } return counter; } template std::string quoted(string&& in) { std::ostringstream ret; ret << '"'; for (char c : in) { if (c < ' ' or c >= '\x7F') { ret << escapify(c); } else if (c == '"') { ret << "\\\""; } else if (c == '\\') { ret << "\\\\"; } else { ret << c; } } ret << '"'; return ret.str(); } // This only uses RTTI because C++ has no other means to get "int" from a // template parameter. template T fromStr(const std::string& val, const char* type = typeid(T).name()) { std::stringstream ss(val); T ret; if (not(ss >> std::boolalpha >> ret).fail()) { return ret; } else { throw std::runtime_error(kblib::quoted(val) + " is not a " + type); } } template <> inline std::string fromStr(const std::string& val, const char*) { return val; } template <> inline bool fromStr(const std::string& val, const char* type) { if (val == "1" or val == "true") { return true; } else if (val == "0" or val == "false") { return false; } else { throw std::runtime_error(kblib::quoted(val) + " is not a " + type); } } template T fromStr(std::string&& val, const char* type = typeid(T).name()) { std::stringstream ss(val); T ret; if (not(ss >> std::boolalpha >> ret).fail()) { return ret; } else { throw std::runtime_error(kblib::quoted(val) + " is not a " + type); } } template <> inline std::string fromStr(std::string&& val, const char*) { return std::move(val); } template <> inline bool fromStr(std::string&& val, const char* type) { if (val == "1" or val == "true") { return true; } else if (val == "0" or val == "false") { return false; } else { throw std::runtime_error(kblib::quoted(val) + " is not a " + type); } } #if KBLIB_USE_STRING_VIEW template <> inline std::string_view fromStr(const std::string& val, const char*) { return val; } template <> inline std::string_view fromStr(std::string&&, const char*) = delete; template T fromStr(std::string_view val, const char* type = typeid(T).name()) { std::istrstream ss(val.data(), kblib::to_signed(val.size())); T ret; if (not(ss >> std::boolalpha >> ret).fail()) { return ret; } else { throw std::runtime_error(kblib::quoted(val) + " is not a " + type); } } template <> inline std::string_view fromStr(std::string_view val, const char*) { return val; } template <> inline std::string fromStr(std::string_view val, const char*) { return std::string(val); } template <> inline bool fromStr(std::string_view val, const char* type) { if (val == "1" or val == "true") { return true; } else if (val == "0" or val == "false") { return false; } else { throw std::runtime_error("\"" + std::string(val) + "\" is not a " + type); } } template To fromStr(const char (&val)[N], const char* type = typeid(To).name()) { // N - 1: remove null terminator return fromStr(std::string_view(val, N - 1), type); } template To fromStr(const char* val, const char* type = typeid(To).name(), _ = 0) { return fromStr(std::string_view(val), type); } #endif template std::string toStr(T val) { std::stringstream ss; ss << val; return ss.str(); } inline std::string toStr(std::string val) { return val; } template struct lexical_caster { static To cast(const From& val, const char* type) { std::stringstream ss; ss << val; To ret; if (not(ss >> ret).fail()) { return ret; } else { throw std::runtime_error("Cannot convert \"" + toStr(val) + "\" to " + type); } } }; template struct lexical_caster { static Same cast(const Same& val, const char*) { return val; } }; template <> struct lexical_caster { static std::string cast(const std::string& val, const char*) { return val; } }; template struct lexical_caster { static std::string cast(const From& val, const char*) { return toStr(val); } }; template struct lexical_caster { static To cast(const std::string& val, const char* type) { return fromStr(val, type); } }; #if KBLIB_USE_STRING_VIEW template <> struct lexical_caster { static std::string_view cast(const std::string_view& val, const char*) { return val; } }; template <> struct lexical_caster { static std::string_view cast(const std::string& val, const char*) { return val; } }; template struct lexical_caster { static std::enable_if_t, std::string_view> cast(const From& val, const char*) { return From(val); } // DCL50-CPP-EX2: // As stated in the normative text, C-style variadic functions that are // declared but never defined are permitted. std::string_view cast(...) = delete; }; template struct lexical_caster { static To cast(std::string_view val, const char* type) { return fromStr(val, type); } }; #endif template To lexical_cast(const From& val, const char* type = typeid(To).name()) { return lexical_caster::cast(val, type); } #if 0 template To lexical_cast(const From& val, const char* type = typeid(To).name()) { using namespace std::literals; if constexpr (std::is_same_v, std::decay_t>) { return val; } else if constexpr (std::is_same_v, std::string>) { return toStr(val); } else if constexpr (std::is_same_v, std::string>) { return fromStr(val, type); } else { std::stringstream ss; ss << val; To ret; if (not (ss >> ret).fail()) return ret; else throw std::runtime_error("Cannot convert \""s + toStr(val) + "\" to " + type); } } #endif } // namespace kblib #endif // KBLIB_CONVERT_H