// // 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 #if __has_include() #include #include #endif #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 ctor_cast(Out & out, std::string_view str) noexcept { auto [rval, found] = cast(str); if (found) { out = Out(std::move(rval)); } return found; } template bool cast_tuple(std::vector const & str, Tuple & to, std::index_sequence) noexcept { return ((cast(std::get(to), str[Is])) && ...); } 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(); } inline std::vector keyval(std::string_view input) noexcept { if (size_t const pos = input.find('='); pos < input.size()) { return {input.substr(0, pos), input.substr(pos + 1)}; } return {input}; } inline bool cast_bool(bool & out, std::string_view str) noexcept { if (any_of(str, "true", "TRUE", "YES", "1")) { out = true; return true; } else if (any_of(str, "false", "FALSE", "NO", "0")) { out = false; return true; } return false; } } // This should be placed last in the file namespace string_utils { template bool cast(Out & out, std::string_view str) noexcept { if constexpr (std::is_same_v) { return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtol, 10)); } else if constexpr (std::is_same_v) { return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtoul, 10)); } else if constexpr (std::is_same_v) { return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtoll, 10)); } else if constexpr (std::is_same_v) { return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtoull, 10)); } else if constexpr (std::is_same_v) { return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtof)); } else if constexpr (std::is_same_v) { return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtod)); } else if constexpr (std::is_same_v) { return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtold)); } else if constexpr (std::is_same_v) { return detail::cast_bool(out, str); } else if constexpr (std::is_same_v) { out = str[0]; return str.size() == 1; } else if constexpr (std::is_constructible_v) { out = Out(str); return true; #if __has_include() } else if constexpr (std::is_enum_v) { std::optional result; if constexpr (magic_enum::detail::subtype_v == magic_enum::detail::enum_subtype::flags) { result = magic_enum::enum_flags_cast(str); } else { result = magic_enum::enum_cast(str); } out = result.value_or(out); return result.has_value(); #endif } else if constexpr (std::is_integral_v) { using V = std::conditional_t, unsigned long, long>; auto [tmp, success] = cast(str); out = static_cast(tmp); return success && tmp == static_cast(out); } else { static_assert(detail::always_false{}, "No match for cast(string)"); } } template bool cast(std::variant & out, std::string_view str) noexcept { return (detail::ctor_cast(out, str) || ...); } template bool cast(std::optional & out, std::string_view str) noexcept { return detail::ctor_cast(out, str) || true; } template bool cast(Out & out, std::vector const & strs) noexcept { if constexpr (detail::is_associative_v) { for (auto elem : strs) { auto [tmp, success] = cast(detail::keyval(elem)); if (!success) { return false; } out.insert(std::move(tmp)); } return true; } else if constexpr (detail::is_container_v) { for (auto elem : strs) { auto [tmp, success] = cast(elem); if (!success) { return false; } out.insert(out.end(), std::move(tmp)); } return true; } else if constexpr (detail::is_tuple_v) { constexpr size_t N = std::tuple_size_v; return strs.size() == N && detail::cast_tuple(strs, out, std::make_index_sequence{}); } else { static_assert(detail::always_false{}, "No match for cast(vector)"); } } template bool cast(Out & out, std::vector const & strs) noexcept { return cast(out, std::vector(strs.begin(), strs.end())); } template std::pair cast(std::string_view str) noexcept { std::pair, bool> rval; using ::string_utils::cast; rval.second = cast(rval.first, str); return rval; } template std::pair cast(std::vector const & strs) noexcept { std::vector tmp(strs.begin(), strs.end()); std::pair, bool> rval; using ::string_utils::cast; rval.second = cast(rval.first, tmp); return rval; } } #undef CAST_NUMBER_IMPL