// // cast.h // string-utils // // Created by Sam Jaffe on 2/13/21. // Copyright © 2021 Sam Jaffe. All rights reserved. // #pragma once #include #include #include #include #include #include #include #include #include "string_utils/any_of.h" #include "string_utils/forwards.h" #include "string_utils/traits.h" #define SAFE_NUMBER_PARSE(func, ...) \ [](char const *in, char **out) { return func(in, out, ##__VA_ARGS__); } namespace string_utils::detail { template bool cast_number(std::string_view str, T & to, F func) noexcept; template std::vector keyval(S const &input); } namespace string_utils { inline bool cast(std::string_view str, std::string & to) noexcept { to = std::string(str); return true; } inline bool cast(std::string_view str, long & to) noexcept { return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtol, 10)); } inline bool cast(std::string_view str, long long & to) noexcept { return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtoll, 10)); } inline bool cast(std::string_view str, float & to) noexcept { return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtof)); } inline bool cast(std::string_view str, double & to) noexcept { return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtod)); } inline bool cast(std::string_view str, long double & to) noexcept { return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtold)); } inline bool cast(std::string_view str, int & to) noexcept { auto [tmp, success] = cast(str); to = static_cast(tmp); return success && tmp == static_cast(to); } inline bool cast(std::string_view str, bool & to) noexcept { if (any_of(str, "true", "TRUE", "YES", "1")) { to = true; return true; } else if (any_of(str, "false", "FALSE", "NO", "0")) { to = false; return true; } return false; } } namespace string_utils { template bool maybe_cast(S const & str, T & to) noexcept { auto [rval, found] = cast(str); if (found) { to = std::move(rval); } return found; } template struct cast_helper{}>> { template bool operator()(std::vector const &strs, T & to) const noexcept { for (S const &elem : strs) { if constexpr (detail::is_associative{}) { auto [tmp, success] = cast(detail::keyval(elem)); if (!success) { return false; } to.insert(std::move(tmp)); } else { auto [tmp, success] = cast(elem); if (!success) { return false; } to.insert(to.end(), std::move(tmp)); } } return true; } }; template struct cast_helper{}>> { template bool cast_tuple(S const & str, T & to, std::index_sequence) const noexcept { return ((cast(str[Is], std::get(to))) && ...); } template bool operator()(std::vector const &strs, T & to) const noexcept { constexpr size_t N = std::tuple_size_v; return strs.size() == N && cast_tuple(strs, to, std::make_index_sequence{}); } }; template struct cast_helper>> { bool operator()(std::string_view str, T & to) const noexcept { to = str; return true; } }; template struct cast_helper> { bool operator()(std::string_view str, std::variant & to) const noexcept { return (maybe_cast(str, to) || ...); } }; template struct cast_helper> { bool operator()(std::string_view str, std::optional & to) const noexcept { return maybe_cast(str, to) || true; } }; } namespace string_utils::detail { template bool cast_tuple(std::vector const & str, Tuple & to, std::index_sequence) noexcept { return ((cast(str[Is], std::get(to))) && ...); } template bool cast_number(std::string_view str, T & to, F func) noexcept { char *counter = nullptr; to = func(str.data(), &counter); return counter == str.end(); } template std::vector keyval(S const &input) { size_t const pos = input.find('='); return pos == S::npos ? std::vector{input} : std::vector{input.substr(0, pos), input.substr(pos + 1)}; } } // This should be placed last in the file namespace string_utils { template std::pair cast(std::string_view str) noexcept { std::pair, bool> rval; rval.second = cast(str, rval.first); return rval; } template std::pair cast(std::vector const & strs) noexcept { std::pair, bool> rval; rval.second = cast(strs, rval.first); return rval; } } #undef CAST_NUMBER_IMPL