// // 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 "any_of.h" #define SAFE_NUMBER_PARSE(func, ...) \ [](char const *in, char **out) { return func(in, out, ##__VA_ARGS__); } namespace string_utils { // A helper object for providing partial specializations for casting template struct cast_helper {}; // The main parser template std::pair cast(std::string const & str) noexcept; template bool cast(std::string const & str, T & to) noexcept; // A section of multi-argument parsers template bool cast(std::vector const & str, std::tuple & to) noexcept; template bool cast(std::vector const & str, std::pair & to) noexcept; } namespace string_utils::detail { template struct has_cast_helper : std::false_type {}; template struct has_cast_helper(std::string, T)>>> : std::true_type {}; template constexpr bool has_cast_helper_v = has_cast_helper{}; } namespace string_utils::detail { template bool cast_tuple(std::vector const & str, Tuple & to) noexcept; template bool cast_number(std::string const &str, T & to, F func) noexcept; } namespace string_utils { template bool cast(std::string const & str, T & to) noexcept { if constexpr (detail::has_cast_helper_v) { return cast_helper{}(str, to); } else if constexpr (std::is_same_v) { to = T(str); } else { static_assert(!detail::has_cast_helper_v, "no cast available"); } return true; } template bool maybe_cast(std::string const & str, T & to) noexcept { auto [rval, found] = cast(str); if (found) { to = std::move(rval); } return found; } template struct cast_helper> { bool operator()(std::string const &str, std::variant & to) const noexcept { return (maybe_cast(str, to) || ...); } }; template struct cast_helper> { bool operator()(std::string const &str, std::optional & to) const noexcept { return maybe_cast(str, to) || true; } }; } namespace string_utils { inline bool cast(std::string const &str, long & to) noexcept { return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtol, 10)); } inline bool cast(std::string const &str, long long & to) noexcept { return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtoll, 10)); } inline bool cast(std::string const &str, float & to) noexcept { return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtof)); } inline bool cast(std::string const &str, double & to) noexcept { return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtod)); } inline bool cast(std::string const &str, long double & to) noexcept { return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtold)); } inline bool cast(std::string const & str, int & to) noexcept { long tmp; bool rval = cast(str, tmp); to = static_cast(tmp); return rval && tmp == static_cast(to); } inline bool cast(std::string const & 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 cast(std::vector const & str, std::tuple & to) noexcept { return detail::cast_tuple(str, to); } template bool cast(std::vector const & str, std::pair & to) noexcept { return detail::cast_tuple(str, to); } } namespace string_utils::detail { template bool cast_tuple(std::vector const & str, Tuple & to, std::index_sequence) { return ((cast(str[Is], std::get(to))) && ...); } template bool cast_tuple(std::vector const & str, Tuple & to) noexcept { constexpr size_t N = std::tuple_size_v; return str.size() == N && cast_tuple(str, to, std::make_index_sequence{}); } template bool cast_number(std::string const &str, T & to, F func) noexcept { char *counter = nullptr; to = func(str.c_str(), &counter); return counter == str.c_str() + str.length(); } } // This should be placed last in the file namespace string_utils { template std::pair cast(std::string const & str) noexcept { using string_utils::cast; std::pair rval; rval.second = cast(str, rval.first); return rval; } } #undef CAST_NUMBER_IMPL