C++17 have <charconv>. However this header does not work on MacOS - It compiles, but can not link. Same header is not present in MSVS. The header also missing from my Arm Linux, even my gcc compiler there supports C++17 as well.
Following is a take to convert non terminated char array (e.g. buffer, string_view etc) into signed or unsigned integer.
I probably will use the code in production, once I do some more tests.
#include <limits>
#include <type_traits>
#include <string_view>
#include <utility>
namespace impl{
template<typename T>
T x10(T a){
return a * 10;
}
inline char digit(char c){
return c - '0';
}
inline bool is_space(char c){
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
inline bool is_sign(char c){
return c == '+' || c == '-';
}
inline bool is_digit(char c){
return c >= '0' && c <= '9';
}
template<typename T>
std::pair<bool,T> convert_unsigned(const char *c, const char *e){
T num = 0;
while(c != e && is_digit(*c)){
T tmp = x10(num) + digit(*c);
if (tmp < num)
return { false, num };
num = tmp;
++c;
}
return { true, num };
}
template<typename T>
std::pair<bool,T> convert(const char *c, const char *e, std::true_type /* unsigned */){
if (c != e && *c == '+')
++c;
return convert_unsigned<T>(c, e);
}
template<typename T>
std::pair<bool,T> convert(const char *c, const char *e, std::false_type /* unsigned */){
using U = std::make_unsigned_t<T>;
T sign = +1;
U max = std::numeric_limits<T>::max();
if (c != e && is_sign(*c)){
if (*c == '-'){
sign = -1;
max = - (U) std::numeric_limits<T>::min();
}
++c;
}
// std::cout << ">>> " << sign << ' ' << max << ' ' << '\n';
auto [ ok, result ] = convert_unsigned<U>(c, e);
if (result > max)
return { false, (T) result };
if (result == max)
return { true, (T) result };
return { ok, sign * (T) result };
}
}
template<typename T>
std::pair<bool,T> convert(std::string_view const s){
static_assert(std::is_integral_v<T>);
return impl::convert<T>(std::begin(s), std::end(s), std::is_unsigned<T>{});
}
#include <cstdint>
#include <iostream>
template<typename T>
void test(const char *s, T correct){
auto [ok, i] = convert<T>(s);
if (!ok)
std::cout << "Err" << '\n';
std::cout << ( i == correct ? "OK" : "NO") << ' ' << i << '\n';
}
int main(){
test<uint16_t> ("-0", std::numeric_limits<uint16_t>::min() );
test<uint16_t> ("65535", std::numeric_limits<uint16_t>::max() );
test<int16_t> ("+32767", std::numeric_limits<int16_t>::max() );
test<int16_t> ("-32768", std::numeric_limits<int16_t>::min() );
test<int16_t> ("+0", 0 );
test<int16_t> ("-0", 0 );
test<int16_t> ("+10", +10 );
test<int16_t> ("-10", -10 );
test<uint64_t> ("+0", std::numeric_limits<uint64_t>::min() );
test<uint64_t> ("+18446744073709551615", std::numeric_limits<uint64_t>::max() );
test<int64_t> ("+9223372036854775807", std::numeric_limits<int64_t>::max() );
test<int64_t> ("-9223372036854775808", std::numeric_limits<int64_t>::min() );
}