Add a C++17 compatible charconv, from_chars only

pull/926/head
Hleb Valoshka 2021-02-27 13:54:33 +02:00
parent 95c24446c1
commit bb9551bcf5
4 changed files with 214 additions and 0 deletions

View File

@ -1,4 +1,6 @@
set(CELCOMPAT_SOURCES
cc.cpp
cc.h
fs.cpp
fs.h
)

View File

@ -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() };
}
}

132
src/celcompat/cc.h 100644
View File

@ -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);
}
}

View File

@ -0,0 +1,9 @@
#pragma once
#include <config.h>
#ifdef HAVE_CHARCONV
#include <charconv>
#else
#include "cc.h"
#endif