mirror of
https://github.com/red0124/ssp.git
synced 2025-01-23 13:05:20 +01:00
251 lines
6.1 KiB
C++
251 lines
6.1 KiB
C++
#pragma once
|
|
|
|
#include "type_traits.hpp"
|
|
#include <charconv>
|
|
#include <cstring>
|
|
#include <functional>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <variant>
|
|
|
|
#ifndef SSP_DISABLE_FAST_FLOAT
|
|
#include <fast_float/fast_float.h>
|
|
#else
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#endif
|
|
|
|
namespace ss {
|
|
|
|
////////////////
|
|
// number converters
|
|
////////////////
|
|
|
|
#ifndef SSP_DISABLE_FAST_FLOAT
|
|
|
|
template <typename T>
|
|
std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
|
|
const char* const begin, const char* const end) {
|
|
T ret;
|
|
auto [ptr, ec] = fast_float::from_chars(begin, end, ret);
|
|
|
|
if (ec != std::errc() || ptr != end) {
|
|
return std::nullopt;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#else
|
|
|
|
template <typename T>
|
|
std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
|
|
const char* const begin, const char* const end) {
|
|
static_assert(!std::is_same_v<T, long double>,
|
|
"Conversion to long double is disabled");
|
|
|
|
constexpr static auto buff_max = 64;
|
|
char short_buff[buff_max];
|
|
size_t string_range = std::distance(begin, end);
|
|
std::string long_buff;
|
|
|
|
char* buff;
|
|
if (string_range > buff_max) {
|
|
long_buff = std::string{begin, end};
|
|
buff = long_buff.data();
|
|
} else {
|
|
buff = short_buff;
|
|
buff[string_range] = '\0';
|
|
std::copy_n(begin, string_range, buff);
|
|
}
|
|
|
|
T ret;
|
|
char* parse_end = nullptr;
|
|
|
|
if constexpr (std::is_same_v<T, float>) {
|
|
ret = std::strtof(buff, &parse_end);
|
|
} else if constexpr (std::is_same_v<T, double>) {
|
|
ret = std::strtod(buff, &parse_end);
|
|
}
|
|
|
|
if (parse_end != buff + string_range) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif
|
|
|
|
////////////////
|
|
// numeric_wrapper
|
|
////////////////
|
|
|
|
template <typename T>
|
|
struct numeric_wrapper {
|
|
using type = T;
|
|
|
|
numeric_wrapper() = default;
|
|
numeric_wrapper(numeric_wrapper&&) = default;
|
|
numeric_wrapper(const numeric_wrapper&) = default;
|
|
|
|
numeric_wrapper& operator=(numeric_wrapper&&) = default;
|
|
numeric_wrapper& operator=(const numeric_wrapper&) = default;
|
|
|
|
numeric_wrapper(T other) : value{other} {
|
|
}
|
|
|
|
operator T() {
|
|
return value;
|
|
}
|
|
|
|
operator T() const {
|
|
return value;
|
|
}
|
|
|
|
T value;
|
|
};
|
|
|
|
using int8 = numeric_wrapper<int8_t>;
|
|
using uint8 = numeric_wrapper<uint8_t>;
|
|
|
|
template <typename T>
|
|
std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
|
|
const char* const begin, const char* const end) {
|
|
T ret;
|
|
auto [ptr, ec] = std::from_chars(begin, end, ret);
|
|
|
|
if (ec != std::errc() || ptr != end) {
|
|
return std::nullopt;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
template <typename T>
|
|
std::enable_if_t<is_instance_of_v<numeric_wrapper, T>, std::optional<T>> to_num(
|
|
const char* const begin, const char* const end) {
|
|
T ret;
|
|
auto [ptr, ec] = std::from_chars(begin, end, ret.value);
|
|
|
|
if (ec != std::errc() || ptr != end) {
|
|
return std::nullopt;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
////////////////
|
|
// extract
|
|
////////////////
|
|
|
|
namespace error {
|
|
template <typename T>
|
|
struct unsupported_type {
|
|
constexpr static bool value = false;
|
|
};
|
|
} /* namespace */
|
|
|
|
template <typename T>
|
|
std::enable_if_t<!std::is_integral_v<T> && !std::is_floating_point_v<T> &&
|
|
!is_instance_of_v<std::optional, T> &&
|
|
!is_instance_of_v<std::variant, T> &&
|
|
!is_instance_of_v<numeric_wrapper, T>,
|
|
bool>
|
|
extract(const char*, const char*, T&) {
|
|
static_assert(error::unsupported_type<T>::value,
|
|
"Conversion for given type is not defined, an "
|
|
"\'extract\' function needs to be defined!");
|
|
}
|
|
|
|
template <typename T>
|
|
std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T> ||
|
|
is_instance_of_v<numeric_wrapper, T>,
|
|
bool>
|
|
extract(const char* begin, const char* end, T& value) {
|
|
auto optional_value = to_num<T>(begin, end);
|
|
if (!optional_value) {
|
|
return false;
|
|
}
|
|
value = optional_value.value();
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
std::enable_if_t<is_instance_of_v<std::optional, T>, bool> extract(
|
|
const char* begin, const char* end, T& value) {
|
|
typename T::value_type raw_value;
|
|
if (extract(begin, end, raw_value)) {
|
|
value = raw_value;
|
|
} else {
|
|
value = std::nullopt;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename T, size_t I>
|
|
bool extract_variant(const char* begin, const char* end, T& value) {
|
|
using IthType = std::variant_alternative_t<I, std::decay_t<T>>;
|
|
IthType ithValue;
|
|
if (extract<IthType>(begin, end, ithValue)) {
|
|
value = ithValue;
|
|
return true;
|
|
} else if constexpr (I + 1 < std::variant_size_v<T>) {
|
|
return extract_variant<T, I + 1>(begin, end, value);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
std::enable_if_t<is_instance_of_v<std::variant, T>, bool> extract(
|
|
const char* begin, const char* end, T& value) {
|
|
return extract_variant<T, 0>(begin, end, value);
|
|
}
|
|
|
|
////////////////
|
|
// extract specialization
|
|
////////////////
|
|
|
|
template <>
|
|
inline bool extract(const char* begin, const char* end, bool& value) {
|
|
if (end == begin + 1) {
|
|
if (*begin == '1') {
|
|
value = true;
|
|
} else if (*begin == '0') {
|
|
value = false;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
size_t size = end - begin;
|
|
if (size == 4 && std::strncmp(begin, "true", size) == 0) {
|
|
value = true;
|
|
} else if (size == 5 && std::strncmp(begin, "false", size) == 0) {
|
|
value = false;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
inline bool extract(const char* begin, const char* end, char& value) {
|
|
value = *begin;
|
|
return (end == begin + 1);
|
|
}
|
|
|
|
template <>
|
|
inline bool extract(const char* begin, const char* end, std::string& value) {
|
|
value = std::string{begin, end};
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
inline bool extract(const char* begin, const char* end,
|
|
std::string_view& value) {
|
|
value = std::string_view{begin, static_cast<size_t>(end - begin)};
|
|
return true;
|
|
}
|
|
|
|
} /* ss */
|