| 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>
- #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 <typename, typename = void> struct cast_helper {};
- // The main parser
- template <typename T> std::pair<T, bool> cast(std::string_view str) noexcept;
- template <typename T> bool cast(std::string_view str, T & to) noexcept;
- // A section of multi-argument parsers
- template <typename... Ts>
- bool cast(std::vector<std::string> const & str, std::tuple<Ts...> & to) noexcept;
- template <typename K, typename V>
- bool cast(std::vector<std::string> const & str, std::pair<K, V> & to) noexcept;
- }
- namespace string_utils::detail {
- template <typename, typename = void> struct has_cast_helper : std::false_type {};
- template <typename T>
- struct has_cast_helper<T, std::void_t<std::result_of_t<cast_helper<T>(std::string, T)>>>
- : std::true_type {};
- template <typename T> constexpr bool has_cast_helper_v = has_cast_helper<T>{};
- }
- namespace string_utils::detail {
- template <typename Tuple>
- bool cast_tuple(std::vector<std::string> const & str, Tuple & to) noexcept;
- template <typename T, typename F>
- bool cast_number(std::string_view str, T & to, F func) noexcept;
- }
- namespace string_utils {
- template <typename T>
- bool cast(std::string_view str, T & to) noexcept {
- if constexpr (detail::has_cast_helper_v<T>) {
- return cast_helper<T>{}(str, to);
- } else if constexpr (std::is_same_v<T, std::string>) {
- to = T(str);
- } else {
- static_assert(!detail::has_cast_helper_v<T>, "no cast available");
- }
- return true;
- }
- template <typename V, typename T> bool maybe_cast(std::string const & str, T & to) noexcept {
- auto [rval, found] = cast<V>(str);
- if (found) { to = std::move(rval); }
- return found;
- }
- template <typename... Ts>
- struct cast_helper<std::variant<Ts...>> {
- bool operator()(std::string const &str, std::variant<Ts...> & to) const noexcept {
- return (maybe_cast<Ts>(str, to) || ...);
- }
- };
- template <typename T>
- struct cast_helper<std::optional<T>> {
- bool operator()(std::string const &str, std::optional<T> & to) const noexcept {
- return maybe_cast<T>(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<int>(tmp);
- return rval && tmp == static_cast<long>(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 <typename... Ts>
- bool cast(std::vector<std::string> const & str, std::tuple<Ts...> & to) noexcept {
- return detail::cast_tuple(str, to);
- }
- template <typename K, typename V>
- bool cast(std::vector<std::string> const & str, std::pair<K, V> & to) noexcept {
- return detail::cast_tuple(str, to);
- }
- }
- namespace string_utils::detail {
- template <typename Tuple, size_t... Is>
- bool cast_tuple(std::vector<std::string> const & str, Tuple & to,
- std::index_sequence<Is...>) {
- return ((cast(str[Is], std::get<Is>(to))) && ...);
- }
- template <typename Tuple>
- bool cast_tuple(std::vector<std::string> const & str, Tuple & to) noexcept {
- constexpr size_t N = std::tuple_size_v<Tuple>;
- return str.size() == N && cast_tuple(str, to, std::make_index_sequence<N>{});
- }
- 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();
- }
- }
- // This should be placed last in the file
- namespace string_utils {
- template <typename T> std::pair<T, bool> cast(std::string_view str) noexcept {
- using string_utils::cast;
- std::pair<T, bool> rval;
- rval.second = cast(str, rval.first);
- return rval;
- }
- }
- #undef CAST_NUMBER_IMPL
|