From: Wolfgang Bangerth Date: Tue, 11 Mar 2025 17:08:02 +0000 (-0600) Subject: Use C++ facilities to convert numbers. X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=182ad380cb6b7cdd3a2a70cf9676d247957f3813;p=dealii.git Use C++ facilities to convert numbers. --- diff --git a/source/base/utilities.cc b/source/base/utilities.cc index e7dd2d05de..9f8b186db3 100644 --- a/source/base/utilities.cc +++ b/source/base/utilities.cc @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include #include #include @@ -597,33 +597,36 @@ namespace Utilities 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; @@ -652,26 +655,29 @@ namespace Utilities 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;