Add a C++17 compatible charconv, from_chars only
parent
95c24446c1
commit
bb9551bcf5
|
@ -1,4 +1,6 @@
|
|||
set(CELCOMPAT_SOURCES
|
||||
cc.cpp
|
||||
cc.h
|
||||
fs.cpp
|
||||
fs.h
|
||||
)
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
// cc.cpp
|
||||
//
|
||||
// Copyright (C) 2021-present, Celestia Development Team.
|
||||
//
|
||||
// from_chars() functions family as in C++17.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
#include "cc.h"
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
auto from_chars(const char* first, const char* last, float &value,
|
||||
std::chars_format fmt)
|
||||
-> std::from_chars_result
|
||||
{
|
||||
char *end;
|
||||
errno = 0;
|
||||
float result = strtof(first, &end);
|
||||
if (result == 0.0f && end == first)
|
||||
return { first, std::errc::invalid_argument };
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
errno = 0;
|
||||
return { first, std::errc::invalid_argument };
|
||||
}
|
||||
value = result;
|
||||
return { end, std::errc() };
|
||||
}
|
||||
|
||||
auto from_chars(const char* first, const char* last, double &value,
|
||||
std::chars_format fmt)
|
||||
-> std::from_chars_result
|
||||
{
|
||||
char *end;
|
||||
errno = 0;
|
||||
double result = strtod(first, &end);
|
||||
if (result == 0.0 && end == first)
|
||||
return { first, std::errc::invalid_argument };
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
errno = 0;
|
||||
return { first, std::errc::invalid_argument };
|
||||
}
|
||||
value = result;
|
||||
return { end, std::errc() };
|
||||
}
|
||||
|
||||
auto from_chars(const char* first, const char* last, long double &value,
|
||||
std::chars_format fmt)
|
||||
-> std::from_chars_result
|
||||
{
|
||||
char *end;
|
||||
errno = 0;
|
||||
double result = strtold(first, &end);
|
||||
if (result == 0.0l && end == first)
|
||||
return { first, std::errc::invalid_argument };
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
errno = 0;
|
||||
return { first, std::errc::invalid_argument };
|
||||
}
|
||||
value = result;
|
||||
return { end, std::errc() };
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
// cc.h
|
||||
//
|
||||
// Copyright (C) 2021-present, Celestia Development Team.
|
||||
//
|
||||
// from_chars() functions family as in C++17.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
|
||||
namespace std
|
||||
{
|
||||
struct from_chars_result
|
||||
{
|
||||
const char* ptr;
|
||||
std::errc ec;
|
||||
};
|
||||
|
||||
enum class chars_format
|
||||
{
|
||||
scientific = 1,
|
||||
fixed = 2,
|
||||
hex = 4,
|
||||
general = fixed | scientific
|
||||
};
|
||||
|
||||
template <typename T, typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type = 0>
|
||||
auto from_chars(const char* first, const char* last, T& value, int base = 10)
|
||||
-> std::from_chars_result;
|
||||
|
||||
auto from_chars(const char* first, const char* last, float &value,
|
||||
std::chars_format fmt = std::chars_format::general)
|
||||
-> std::from_chars_result;
|
||||
|
||||
auto from_chars(const char* first, const char* last, double &value,
|
||||
std::chars_format fmt = std::chars_format::general)
|
||||
-> std::from_chars_result;
|
||||
|
||||
auto from_chars(const char* first, const char* last, long double &value,
|
||||
std::chars_format fmt = std::chars_format::general)
|
||||
-> std::from_chars_result;
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
auto int_from_chars(const char* first, const char* last, T& value, int base) noexcept
|
||||
-> std::from_chars_result
|
||||
{
|
||||
auto p = first;
|
||||
bool has_minus = false;
|
||||
if (std::numeric_limits<T>::is_signed && *p == '-')
|
||||
{
|
||||
has_minus = true;
|
||||
p++;
|
||||
}
|
||||
|
||||
using U = typename make_unsigned<T>::type;
|
||||
|
||||
U risky_value;
|
||||
U max_digit;
|
||||
if (has_minus)
|
||||
{
|
||||
risky_value = (static_cast<U>(std::numeric_limits<T>::max()) + 1) / base;
|
||||
max_digit = (static_cast<U>(std::numeric_limits<T>::max()) + 1) % base;
|
||||
}
|
||||
else
|
||||
{
|
||||
risky_value = static_cast<U>(std::numeric_limits<T>::max() / base);
|
||||
max_digit = static_cast<U>(std::numeric_limits<T>::max() % base);
|
||||
}
|
||||
|
||||
char last_n = '0' + base;
|
||||
char last_ll = 'a' + base - 10;
|
||||
char last_ul = 'A' + base - 10;
|
||||
|
||||
bool overflow = false;
|
||||
T result = 0;
|
||||
for (; p < last; p++)
|
||||
{
|
||||
U digit;
|
||||
if (base <= 10)
|
||||
{
|
||||
if (*p < '0' || *p >= last_n)
|
||||
break;
|
||||
digit = static_cast<U>(*p - '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*p >= '0' && *p <= '9')
|
||||
digit = static_cast<U>(*p - '0');
|
||||
else if (*p >= 'a' && *p < last_ll)
|
||||
digit = static_cast<U>(*p - 'a' + 10);
|
||||
else if (*p >= 'A' && *p < last_ul)
|
||||
digit = static_cast<U>(*p - 'A' + 10);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (result > risky_value || (result == risky_value && digit > max_digit))
|
||||
overflow = true;
|
||||
|
||||
result = result * base + digit;
|
||||
}
|
||||
if (p == first || (has_minus && p == first + 1))
|
||||
return { first, std::errc::invalid_argument };
|
||||
|
||||
if (overflow)
|
||||
return { p, std::errc::result_out_of_range };
|
||||
|
||||
if (has_minus)
|
||||
value = -1 - static_cast<T>(result - 1);
|
||||
else
|
||||
value = static_cast<T>(result);
|
||||
return { p, std::errc() };
|
||||
}
|
||||
|
||||
} // anon namespace
|
||||
|
||||
template <typename T, typename std::enable_if<std::numeric_limits<T>::is_integer, T>::type>
|
||||
auto from_chars(const char* first, const char* last, T& value, int base)
|
||||
-> std::from_chars_result
|
||||
{
|
||||
return int_from_chars(first, last, value, base);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#ifdef HAVE_CHARCONV
|
||||
#include <charconv>
|
||||
#else
|
||||
#include "cc.h"
|
||||
#endif
|
Loading…
Reference in New Issue