| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- //
- // cast.h
- // string-utils
- //
- // Created by Sam Jaffe on 2/13/21.
- // Copyright © 2021 Sam Jaffe. All rights reserved.
- //
- #pragma once
- #include <cstdlib>
- #include <string>
- #include <type_traits>
- #include <utility>
- #if __has_include(<variant>) && __cplusplus > 201402L
- #define STRING_UTIL_CAST_STD_VARIANT
- #include <variant>
- #endif
- #include "any_of.h"
- #define CAST_NUMBER_IMPL(type, func, ...) \
- inline bool cast(std::string const & str, type & to) { \
- char * rval; \
- to = func(str.c_str(), &rval, ##__VA_ARGS__); \
- return rval == str.c_str() + str.length(); \
- }
- namespace string_utils { namespace traits {
- template <typename T, typename = void> struct is_stringy : std::false_type {};
- #if defined(STRING_UTIL_CAST_STD_VARIANT)
- template <typename> struct is_variant : std::false_type {};
- template <typename... Ts>
- struct is_variant<std::variant<Ts...>> : std::true_type {};
- template <typename T>
- struct is_stringy<
- T, std::enable_if_t<std::is_constructible<T, std::string const &>{} &&
- !is_variant<T>{}>> : std::true_type {};
- #else
- template <typename T>
- struct is_stringy<
- T, std::enable_if_t<std::is_constructible<T, std::string const &>{}>>
- : std::true_type {};
- #endif
- }}
- namespace string_utils {
- template <typename T> std::pair<T, bool> cast(std::string const & str);
- template <typename T, typename = std::enable_if_t<traits::is_stringy<T>{}>>
- bool cast(std::string const & str, T & to) {
- to = T(str);
- return true;
- }
- CAST_NUMBER_IMPL(long, std::strtol, 10);
- CAST_NUMBER_IMPL(long long, std::strtoll, 10);
- CAST_NUMBER_IMPL(float, std::strtof);
- CAST_NUMBER_IMPL(double, std::strtod);
- CAST_NUMBER_IMPL(long double, std::strtold);
- inline bool cast(std::string const & str, int & to) {
- long tmp;
- bool rval = cast(str, tmp);
- to = static_cast<int>(tmp);
- return rval && tmp == static_cast<long>(to);
- }
- inline bool cast(std::string const & str, bool & to) {
- 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;
- }
- }
- #if defined(STRING_UTIL_CAST_STD_VARIANT)
- namespace string_utils::detail {
- template <size_t I, typename... Ts>
- bool _cast(std::string const & str, std::variant<Ts...> & to) {
- auto [rval, found] = cast<std::tuple_element_t<I, std::tuple<Ts...>>>(str);
- if (found) { to = std::move(rval); }
- return found;
- }
- // This is needed due to a compiler bug in Clang that fails to properly compile
- // fold-expressions.
- template <size_t I, typename... Ts>
- bool _cast_chain(std::string const & str, std::variant<Ts...> & to) {
- if constexpr (I == sizeof...(Ts)) {
- return false;
- } else {
- return _cast<I>(str, to) || _cast_chain<I + 1>(str, to);
- }
- }
- }
- namespace string_utils {
- template <typename... Ts>
- bool cast(std::string const & str, std::variant<Ts...> & to) {
- return detail::_cast_chain<0>(str, to);
- }
- }
- #endif
- // This should be placed last in the file
- namespace string_utils {
- template <typename T> std::pair<T, bool> cast(std::string const & str) {
- using string_utils::cast;
- std::pair<T, bool> rval;
- rval.second = cast(str, rval.first);
- return rval;
- }
- }
- #undef CAST_NUMBER_IMPL
|