Component of the Week #5: bdlb_numericparseutil

Summary:
  • Provides functions for efficient and safe parsing of integral and floating-point numbers.

Just like the task of converting numbers to text that we looked at in the previous week’s Component of the Week #4: bslalg_numericformatterutil, the opposite task - converting text to numbers - is a very common operation. The C++ Standard Library provides a few option to perform this operation, but all of them have some drawbacks. atoi only handles radix 10 and could lead to undefined behavior, sscanf and istream are far too flexible to be the most efficient, bsl::stoi requires a bsl::string as an input and requires exception handling. The other standard library alternative available in modern compilers is from_chars, which, like NumericParseUtil is low-level and efficient. In comparison to from_chars, NumericParseUtil supports C++03 and has an extension that may be important: in addition to returning an ERANGE error, it also sets the output value in case an overflow (to INF) or underflow (to 0) occurs.

Suppose we want to parse a 64-bit signed integer that we know is supposed to arrive to us from the remote in a hex format. All we need to do is have the textual representation and pass it to bdlb::NumericParseUtil::parseInt64:

typedef bdlb::NumericParseUtil NPUtil;

long long value;
int rc = NPUtil::parseInt64(&value, "-deadbeef", 16);

if (0 == rc) {
    bsl::cout << value;
} else {
    bsl::cerr << "Well, that's surprising - we're parsing a known good value!\n";
}

The parsing will only fail if the string is empty, or the first character is bad. Otherwise, the parsing will succeed and will process all characters that can be interpreted as part of the number. For example, parsing "123_?!" will return success and the number 123, stopping at the _ character. We can receive the information about where the parsing has stopped by supplying an additional remainder argument to parse:

typedef bdlb::NumericParseUtil NPUtil;

long long value;
bsl::string_view remainder;
int rc = NPUtil::parseInt64(&value, &remainder, "-deadbeef? Really?",  16);

if (0 == rc) {
    bsl::cout << "Value: " << value << " Remainder:'" << remainder << "'\n";
} else {
    bsl::cerr << "Something's really not right! Cosmic ray?\n";
}

The code for parsing a double is very similar:

double value;
int rc = NPUtil::parseDouble(&value, "6.62607015e-34");

if (0 == rc) {
    bsl::cout << value;
} else {
    bsl::cerr << "What is going on???\n";
}

bdlb::NumericParseUtil::parseDouble will happily handle decimal and scientific representations (but will reject hex-floats), special values like NaN and infinity, and will even let you know if it parsed a number so small or so big that it can’t be represented by a double, while helpfully setting the output value to zero or infinity respectively!

Check out the documentation for bdlb_numericparseutil for many more details.