October 14th, 2024

C++ String Conversion: Exploring std:from_chars in C++17 to C++26

The article outlines the evolution of `std::from_chars` in C++ from C++17 to C++26, highlighting its performance benefits for string-to-number conversions and its utility in applications like JSON parsing.

Read original articleLink Icon
C++ String Conversion: Exploring std:from_chars in C++17 to C++26

The article discusses the introduction and evolution of the `std::from_chars` function in C++ from C++17 to C++26, which enhances string-to-number conversion capabilities. This function provides a low-level, high-performance alternative to older methods like `atoi` and `stringstream`, offering non-throwing, non-allocating, and memory-safe conversions without locale support. The API includes functions for both integral and floating-point types, returning a `from_chars_result` structure that indicates the success of the conversion and provides error information. Performance benchmarks show that `std::from_chars` is significantly faster than previous methods, with improvements noted in C++23 and upcoming features in C++26, such as constexpr support and simplified error checking. The article emphasizes the utility of `std::from_chars` for applications requiring efficient parsing, such as JSON and 3D model formats, and highlights the importance of compiler support across different platforms.

- `std::from_chars` offers a high-performance alternative for string-to-number conversions in C++.

- It provides detailed error reporting and is designed for both integral and floating-point types.

- Performance benchmarks indicate it is significantly faster than traditional methods like `atoi` and `stringstream`.

- C++23 introduces constexpr support, while C++26 will enhance error checking capabilities.

- The function is particularly useful for parsing tasks in applications like JSON and 3D modeling.

Link Icon 9 comments
By @dgrunwald - 6 months
Caution with these functions: in most cases you need to check not only the error code, but also the `ptr` in the result. Otherwise you end up with `to_int("0x1234") == 0` instead of the expected `std::nullopt`, because these functions return success if they matched a number at the beginning of the string.
By @captainmuon - 6 months
I wonder why it is called `from_chars` and not `to_number` or similar. It's obvious what you are converting from, because you have to write down the argument `from_chars(someString)`, but you don't see what is coming out.
By @vaylian - 6 months
Example from the website:

  const std::string str { "12345678901234" };
  int value = 0;
  std::from_chars(str.data(),str.data() + str.size(), value);

On the third line: Why can I just pass in `value` like this? Shouldn't I use `&value` to pass in the output variable as a reference?
By @Dwedit - 6 months
What if you try to convert a French floating point number that uses a comma instead of a dot?
By @einpoklum - 6 months
Note that this won't work (AFAICT) with Unicode strings and non-western-arabic digits, e.g.:

    std::u8string_view chars{ u8"۱۲۳٤" };
    int value;
    enum { digit_base = 10 };
    auto [ptr, ec] = std::from_chars(
       chars.data(), chars.data() + chars.size(), value, digit_base);
    return (ec == std::errc{}) ? value : -1;
will fail to compile due to pointer incompatibility.
By @cherryteastain - 6 months
Wish they returned std::expected<T, std::errc> instead of the weird from_chars_result struct
By @lynx23 - 6 months
Whoever introduced the rule to automatically delete :: in titles on a hacker site should be made to rethink their decisions. Its a silly rule. It should go.
By @criddell - 6 months
The author lists sprintf as one of the ways you can convert a string to a numbers. How would that work?
By @userbinator - 6 months
Wasn’t the old stuff good enough? Why do we need new methods? In short: because from_chars is low-level, and offers the best possible performance.

That sounds like marketing BS, especially when most likely these functions just call into or are implemented nearly identically to the old C functions which are already going to "offers the best possible performance".

I did some benchmarks, and the new routines are blazing fast![...]around 4.5x faster than stoi, 2.2x faster than atoi and almost 50x faster than istringstream

Are you sure that wasn't because the compiler decided to optimise away the function directly? I can believe it being faster than istringstream, since that has a ton of additional overhead.

After all, the source is here if you want to look into the horse's mouth:

https://raw.githubusercontent.com/gcc-mirror/gcc/master/libs...

Not surprisingly, under all those layers of abstraction-hell, there's just a regular accumulation loop.