Safely convert std::string_view to int (like stoi or atoi)
Solution 1
The std::from_chars function does not throw, it only returns a value of type from_chars_result
which is a struct with two fields:
struct from_chars_result {
const char* ptr;
std::errc ec;
};
You should inspect the values of ptr
and ec
when the function returns:
#include <iostream>
#include <string>
#include <charconv>
int main()
{
int i3;
std::string_view sv = "abc";
auto result = std::from_chars(sv.data(), sv.data() + sv.size(), i3);
if (result.ec == std::errc::invalid_argument) {
std::cout << "Could not convert.";
}
}
Solution 2
Unfortunately, there is no standard way that would throw an exception for you but std::from_chars
has a return value code that you may use:
#include <charconv>
#include <stdexcept>
template <class T, class... Args>
void from_chars_throws(const char* first, const char* last, T &t, Args... args) {
std::from_chars_result res = std::from_chars(first, last, t, args... );
// These two exceptions reflect the behavior of std::stoi.
if (res.ec == std::errc::invalid_argument) {
throw std::invalid_argument{"invalid_argument"};
}
else if (res.ec == std::errc::result_out_of_range) {
throw std::out_of_range{"out_of_range"};
}
}
Obviously you can create svtoi
, svtol
from this, but the advantage of "extending" from_chars
is that you only need a single templated function.
Solution 3
Building on @Ron and @Holt's excellent answers, here's a small wrapper around std::from_chars()
that returns an optional (std::nullopt
when the input fails to parse).
#include <charconv>
#include <optional>
#include <string_view>
std::optional<int> to_int(const std::string_view & input)
{
int out;
const std::from_chars_result result = std::from_chars(input.data(), input.data() + input.size(), out);
if(result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range)
{
return std::nullopt;
}
return out;
}
Related videos on Youtube
Phil-ZXX
Updated on May 20, 2021Comments
-
Phil-ZXX about 3 years
Is there a safe standard way to convert
std::string_view
toint
?
Since C++11
std::string
lets us usestoi
to convert toint
:std::string str = "12345"; int i1 = stoi(str); // Works, have i1 = 12345 int i2 = stoi(str.substr(1,2)); // Works, have i2 = 23 try { int i3 = stoi(std::string("abc")); } catch(const std::exception& e) { std::cout << e.what() << std::endl; // Correctly throws 'invalid stoi argument' }
But
stoi
does not supportstd::string_view
. So alternatively, we could useatoi
, but one has to be very careful, e.g.:std::string_view sv = "12345"; int i1 = atoi(sv.data()); // Works, have i1 = 12345 int i2 = atoi(sv.substr(1,2).data()); // Works, but wrong, have i2 = 2345, not 23
So
atoi
does not work either, since it is based off the null-terminator'\0'
(and e.g.sv.substr
cannot simply insert/add one).Now, since C++17 there is also
from_chars
, but it does not seem to throw when providing poor inputs:try { int i3; std::string_view sv = "abc"; std::from_chars(sv.data(), sv.data() + sv.size(), i3); } catch (const std::exception& e) { std::cout << e.what() << std::endl; // Does not get called }
-
Yksisarvinen almost 5 yearsThat's because
std::from_chars
does not throw anything. Instead it returns an error code.
-
-
M.M about 3 yearsWhat about when
ec
is some other failure code beside those two? -
s3cur3 about 3 years@M.M, as best I can tell from the CppReference docs, those two are the only error codes that
std::from_chars()
will return. -
303 over 2 yearsWhy not simply return
out
ifec
matchesstd::errc{}
andstd::nullopt
otherwise? Isn't that the lesser verbose alternative? I also think thatstd::string_view
s fall in line with iterators and passing them by value is the way to go.