#include <algorithm>
#include <bitset>
#include <cctype>
-#include <cerrno>
+#include <charconv>
#include <cmath>
#include <cstddef>
#include <cstdio>
int
string_to_int(const std::string &s_)
{
- // trim whitespace on either side of the text if necessary
+ // Trim whitespace on either side of the text if necessary
std::string s = s_;
while ((s.size() > 0) && (s[0] == ' '))
s.erase(s.begin());
while ((s.size() > 0) && (s.back() == ' '))
s.erase(s.end() - 1);
- // Now convert and see whether we succeed. Note that strtol only
- // touches errno if an error occurred, so if we want to check
- // whether an error happened, we need to make sure that errno==0
- // before calling strtol since otherwise it may be that the
- // conversion succeeds and that errno remains at the value it
- // was before, whatever that was.
- char *p;
- errno = 0;
- const int i = std::strtol(s.c_str(), &p, 10);
+ // This function used to be built on top of strtol, which gladly converts
+ // "+1" into int(1). But the std::from_chars() function we use below that
+ // does not. So, if the string starts with a plus, just eat it so that we
+ // stay backward compatible:
+ if ((s.size() > 0) && (s[0] == '+'))
+ s.erase(s.begin());
+
+ // Now convert and see whether we succeed.
+ int i;
+ const std::from_chars_result result =
+ std::from_chars(s.data(), s.data() + s.size(), i, /*base=*/10);
// We have an error if one of the following conditions is true:
- // - strtol sets errno != 0
- // - The original string was empty (we could have checked that
- // earlier already)
- // - The string has non-zero length and strtol converted the
+ // - result.ec is not equal to a default-constructed std::errc(),
+ // for example if the string does not even start with a
+ // number; or
+ // - The string has non-zero length and std::from_chars() converted the
// first part to something useful, but stopped converting short
// of the terminating '\0' character. This happens, for example,
- // if the given string is "1234 abc".
- AssertThrow(!((errno != 0) || (s.empty()) ||
- ((s.size() > 0) && (*p != '\0'))),
+ // if the given string is "1234 abc". In that case,
+ // result.ptr points to something other than the end of
+ // the string (namely, the first character past the number).
+ AssertThrow(result.ec == std::errc() && result.ptr == s.data() + s.size(),
ExcMessage("Can't convert <" + s + "> to an integer."));
return i;
while ((s.size() > 0) && (s.back() == ' '))
s.erase(s.end() - 1);
- // Now convert and see whether we succeed. Note that strtol only
- // touches errno if an error occurred, so if we want to check
- // whether an error happened, we need to make sure that errno==0
- // before calling strtol since otherwise it may be that the
- // conversion succeeds and that errno remains at the value it
- // was before, whatever that was.
- char *p;
- errno = 0;
- const double d = std::strtod(s.c_str(), &p);
+ // This function used to be built on top of strtod, which gladly converts
+ // "+1" into int(1). But the std::from_chars() function we use below that
+ // does not. So, if the string starts with a plus, just eat it so that we
+ // stay backward compatible:
+ if ((s.size() > 0) && (s[0] == '+'))
+ s.erase(s.begin());
+
+ // Now convert and see whether we succeed.
+ double d;
+ const std::from_chars_result result = std::from_chars(
+ s.data(), s.data() + s.size(), d, std::chars_format::general);
// We have an error if one of the following conditions is true:
- // - strtod sets errno != 0
- // - The original string was empty (we could have checked that
- // earlier already)
- // - The string has non-zero length and strtod converted the
+ // - result.ec is not equal to a default-constructed std::errc(),
+ // for example if the string does not even start with a
+ // number; or
+ // - The string has non-zero length and std::from_chars() converted the
// first part to something useful, but stopped converting short
// of the terminating '\0' character. This happens, for example,
- // if the given string is "1.234 abc".
- AssertThrow(!((errno != 0) || (s.empty()) ||
- ((s.size() > 0) && (*p != '\0'))),
+ // if the given string is "1234 abc". In that case,
+ // result.ptr points to something other than the end of
+ // the string (namely, the first character past the number).
+ AssertThrow(result.ec == std::errc() && result.ptr == s.data() + s.size(),
ExcMessage("Can't convert <" + s + "> to a double."));
return d;