// // 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 "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(S const &str) noexcept; template bool cast(std::string_view str, T & to) noexcept; template bool cast(std::vector const &strs, T & to) noexcept; } namespace string_utils::detail { template struct has_cast_helper : std::false_type {}; template struct has_cast_helper(S, T)>>> : std::true_type {}; template struct is_tuple : std::false_type {}; template struct is_tuple::type>> : std::true_type {}; template struct is_associative : std::false_type {}; template struct is_associative> : std::true_type {}; template struct is_container : std::false_type {}; template struct is_container> : std::true_type {}; } namespace string_utils::detail { template bool cast_tuple(std::vector const & str, Tuple & to, std::index_sequence) noexcept; template bool cast_number(std::string_view str, T & to, F func) noexcept; template std::vector keyval(S const &input); } namespace string_utils { template bool cast(std::string_view str, T & to) noexcept { if constexpr (detail::has_cast_helper{}) { return cast_helper{}(str, to); } else if constexpr (std::is_same_v) { to = T(str); } else { static_assert(!detail::has_cast_helper{}, "no cast available"); } return true; } template bool cast(std::vector const &strs, T & to) noexcept { if constexpr(detail::has_cast_helper>{}) { return cast_helper{}(strs, to); } else if constexpr (detail::is_tuple{}) { constexpr size_t N = std::tuple_size_v; return strs.size() == N && detail::cast_tuple(strs, to, std::make_index_sequence{}); } else if constexpr (detail::is_associative{}) { for (S const &elem : strs) { size_t const pos = elem.find('='); auto [key, sk] = cast(elem.substr(0, pos)); auto [value, sv] = cast(elem.substr(pos + 1)); if (pos == S::npos || !sk || !sv) { return false; } to.insert(std::move(key), std::move(value)); } } else if constexpr (detail::is_container{}) { for (S const &elem : strs) { auto [tmp, success] = cast(elem); if (!success) { return false; } to.insert(to.end(), std::move(tmp)); } } else { static_assert(!detail::has_cast_helper>{}, "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_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 { long tmp; bool rval = cast(str, tmp); to = static_cast(tmp); return rval && 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::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(); } } // This should be placed last in the file namespace string_utils { template std::pair cast(S const & str) noexcept { using string_utils::cast; std::pair, bool> rval; rval.second = cast(str, rval.first); return rval; } } #undef CAST_NUMBER_IMPL