| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- //
- // cast.h
- // string-utils
- //
- // Created by Sam Jaffe on 2/13/21.
- // Copyright © 2021 Sam Jaffe. All rights reserved.
- //
- #pragma once
- #include <cstdlib>
- #include <optional>
- #include <string>
- #include <string_view>
- #include <type_traits>
- #include <utility>
- #include <variant>
- #include <vector>
- #if __has_include(<magic_enum/magic_enum.hpp>)
- #include <magic_enum/magic_enum.hpp>
- #include <magic_enum/magic_enum_flags.hpp>
- #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 <typename Actual, typename Out>
- bool ctor_cast(Out & out, std::string_view str) noexcept {
- auto [rval, found] = cast<Actual>(str);
- if (found) { out = Out(std::move(rval)); }
- return found;
- }
- template <typename Tuple, size_t... Is>
- bool cast_tuple(std::vector<std::string_view> const & str, Tuple & to,
- std::index_sequence<Is...>) noexcept {
- return ((cast(std::get<Is>(to), str[Is])) && ...);
- }
- template <typename T, typename F>
- 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<std::string_view> 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 <typename Out> bool cast(Out & out, std::string_view str) noexcept {
- if constexpr (std::is_same_v<Out, long>) {
- return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtol, 10));
- } else if constexpr (std::is_same_v<Out, unsigned long>) {
- return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtoul, 10));
- } else if constexpr (std::is_same_v<Out, long long>) {
- return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtoll, 10));
- } else if constexpr (std::is_same_v<Out, unsigned long long>) {
- return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtoull, 10));
- } else if constexpr (std::is_same_v<Out, float>) {
- return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtof));
- } else if constexpr (std::is_same_v<Out, double>) {
- return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtod));
- } else if constexpr (std::is_same_v<Out, long double>) {
- return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtold));
- } else if constexpr (std::is_same_v<Out, bool>) {
- return detail::cast_bool(out, str);
- } else if constexpr (std::is_same_v<Out, char>) {
- out = str[0];
- return str.size() == 1;
- } else if constexpr (std::is_constructible_v<Out, std::string_view>) {
- out = Out(str);
- return true;
- #if __has_include(<magic_enum/magic_enum.hpp>)
- } else if constexpr (std::is_enum_v<Out>) {
- std::optional<Out> result;
- if constexpr (magic_enum::detail::subtype_v<Out> ==
- magic_enum::detail::enum_subtype::flags) {
- result = magic_enum::enum_flags_cast<Out>(str);
- } else {
- result = magic_enum::enum_cast<Out>(str);
- }
- out = result.value_or(out);
- return result.has_value();
- #endif
- } else if constexpr (std::is_integral_v<Out>) {
- using V = std::conditional_t<std::is_unsigned_v<Out>, unsigned long, long>;
- auto [tmp, success] = cast<V>(str);
- out = static_cast<Out>(tmp);
- return success && tmp == static_cast<V>(out);
- } else {
- static_assert(detail::always_false<Out>{}, "No match for cast(string)");
- }
- }
- template <typename... Ts>
- bool cast(std::variant<Ts...> & out, std::string_view str) noexcept {
- return (detail::ctor_cast<Ts>(out, str) || ...);
- }
- template <typename Out>
- bool cast(std::optional<Out> & out, std::string_view str) noexcept {
- return detail::ctor_cast<Out>(out, str) || true;
- }
- template <typename Out>
- bool cast(Out & out, std::vector<std::string_view> const & strs) noexcept {
- if constexpr (detail::is_associative_v<Out>) {
- for (auto elem : strs) {
- auto [tmp, success] =
- cast<typename Out::value_type>(detail::keyval(elem));
- if (!success) { return false; }
- out.insert(std::move(tmp));
- }
- return true;
- } else if constexpr (detail::is_container_v<Out>) {
- for (auto elem : strs) {
- auto [tmp, success] = cast<typename Out::value_type>(elem);
- if (!success) { return false; }
- out.insert(out.end(), std::move(tmp));
- }
- return true;
- } else if constexpr (detail::is_tuple_v<Out>) {
- constexpr size_t N = std::tuple_size_v<Out>;
- return strs.size() == N &&
- detail::cast_tuple(strs, out, std::make_index_sequence<N>{});
- } else {
- static_assert(detail::always_false<Out>{},
- "No match for cast(vector<string>)");
- }
- }
- template <typename Out, typename S>
- bool cast(Out & out, std::vector<S> const & strs) noexcept {
- return cast(out, std::vector<std::string_view>(strs.begin(), strs.end()));
- }
- template <typename T> std::pair<T, bool> cast(std::string_view str) noexcept {
- std::pair<detail::decay_t<T>, bool> rval;
- using ::string_utils::cast;
- rval.second = cast(rval.first, str);
- return rval;
- }
- template <typename T, typename S>
- std::pair<T, bool> cast(std::vector<S> const & strs) noexcept {
- std::vector<std::string_view> tmp(strs.begin(), strs.end());
- std::pair<detail::decay_t<T>, bool> rval;
- using ::string_utils::cast;
- rval.second = cast(rval.first, tmp);
- return rval;
- }
- }
- #undef CAST_NUMBER_IMPL
|