5 ms·
C++ String Conversion: Exploring std:from_chars in C++17 to C++26
- Dwedit 2y agoWhat if you try to convert a French floating point number that uses a comma instead of a dot?
- jeffbee 2y agoThen the programmer has made a mistake, because the behavior is the same as `strtod` in C locale, i.e. it stops parsing at the first comma. You should think of `from_chars` as a function that accepts the outputs of `to_chars`, not as a general text understander.
- lynx23 2y agoWhoever 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.
- MathMonkeyMan 2y agoIt would be an interesting piece to learn why. Maybe it was for ::reasons::.
- lynx23 2y agoI am sure the intern who wrote a rule for the Apple VoiceOver speech synthesizer to special case the number 18 being pronounced english while the synth voice is set to german imagined to have a good reason at the time as well. However, that desn't make ther decision less stupid. "Vierzehn Uhr", "Fünfzehn Uhr", "Sechzehn Uhr", "Siebzehn Uhr", "Eighteen Uhr".
- pjmlp 2y agoMost of those silly rules can be overriden after submission, there is a timeout to enforce the desired title instead by editing it.
- deleted 2y ago[deleted]
- dgrunwald 2y agoCaution 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.
- pilif 2y agohow can this be the ~5th iteration of a very wide-spread use-case and still contain a footgun? The API looks like it's following best-practice, with a specific result type that also contains specific error information and yet, that's not enough and you still end up with edge-cases where things look like they're fine when they aren't.
- amelius 2y agoI suppose the reason is that sometimes you want to parse more than just the number. For example, numbers separated by commas. In that case you will have to call the function repeatedly from your own parsing routine and advance the pointer.
- gpderetta 2y agoYes, if you are parsing in-place numbers in an existing string you do not necessarily know a-priori where the number end. You could have extra logic to look ahead to the end of the number before passing it from_chars, but a) it would require an additional pass and b) you could end up replicating part of the implementation of from chars. from_chars is supposed to be a low lever routine, it must be possible to use it correctly, but it is not necessarily the most ergonomic.
- khwrht 2y agoThat is a good question. The C++ stdlib has some truly bizarre APIs. I wonder if they should freeze std and work on std2.
- 2y ago
- einpoklum 2y agoNote 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.
- cout 2y agoWhat would you suggest instead?
- deleted 2y ago[deleted]
- jcelerier 2y agostd::from_chars / std::to_chars are explicitly made to only operate in the C locale, so basically ASCII texts. It's not meant for parsing user-input strings but rather text protocols with maximal efficiency (and locale support prevents that). E.g. "۱۲۳٤" isn't as far as I know a valid number to put in a json file, http content-length or CSS property. Maybe it'd be ok in a CSV but realistically have you ever seen a big data csv encoded in non-ascii numbers?
- gpderetta 2y agoThis way your spreadsheet could interpret the data it loads from a CVS differently depending on on the locale of the machine it is running on. Of course nobody would design an application like that.
- einpoklum 2y ago> have you ever seen a big data csv encoded in non-ascii numbers? Let me ask you this: How much data have you processed which comes from human user input in Arabic-speaking countries?
- 2y ago
- cherryteastain 2y agoWish they returned std::expected<T, std::errc> instead of the weird from_chars_result struct
- Longhanks 2y agostd::expected is new in C++23, std::from_chars was introduces in C++17. Obviously, 2023 features were not available in 2017. Changing the return type now would break everybody's code, with very little benefit, you can easily wrap std::from_chars.
- jlarocco 2y agoHaving an inconsistent, special case way of doing something in the name of backwards compatibility is the defining characteristic of C++.
- Night_Thastus 2y agoIt's a tradeoff. For example: With Python Python breaking changes are more common, and everyone complains about how much they have to go and fix every time something changes. Damned if you do, damned if you don't. Either have good backwards compatibility but sloppy older parts of the language - or have a 'cleaner' overall language at the cost of crushing the community every time you want to change anything.
- jlarocco 2y agoI'm fully aware, just pointing out that C++ is particularly afflicted with backward compatibility issues; far more than other languages. "The Design and Evolution of C++" gives the impression that even back in the 80s, major concessions were being made in the name of compatibility. At that time it was with C; now it's with previous versions of C++.
- criddell 2y agoIf returning std::expected makes more sense, why not make it the primary signature and create a wrapper to maintain compatibility with old code?
- userbinator 2y agoWasn’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/libstdc%2B%2B-v3/include/std/charconv 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.
- j16sdiz 2y agoEnabling new static optimization is a good, no?
- OvbiousError 2y agoI agree with some of this, and the author could've made a better case for from/to_chars: - Afaik stoi and friends depend on the locale, so it's not hard to believe this introduced additional overhead. The implicit locale dependency is also often very surprising. - std::stoi only accepts std::string as input, so you're forced to allocate a string to use it. std::from_chars does not. - from/to_chars don't throw. As far as I know this won't affect performance if it doesn't happen, it does mean you can use these functions in environments where exceptions are disabled.
- captainmuon 2y agoI 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.
- Someone 2y agoAs you indicate, you do see what you’re putting in, and that includes an argument holding a reference to the result of the conversion. What’s coming out is a std::from_chars_result: a status code plus an indicator how much of the data was consumed. What to name this depends on how you see this function. As part of a function on number types, from_chars is a good names. As part of a function on strings, to_int/to_long/etc are good names. As freestanding functions, chars_to_int (ugly, IMO), parse_int (with parse sort-of implying taking a string) are options. I can see why they went for from_chars. Implementations will be more dependent on the output type than on character pointers, it’s more likely there will be more integral types in the future than that there will be a different way to specify character sequences, and it means adding a single function name.
- pavlov 2y agoMaybe “number” is too ambiguous because they’d have to define that “in this context a number means a float or integer type only.” The C++ standard also includes support for complex numbers.
- beyondCritics 2y agoAs a matter of fact, this overloads the name and hence gives less hassle for generic coding.
- criddell 2y agoThe author lists sprintf as one of the ways you can convert a string to a numbers. How would that work?
- randomNumber7 2y agoDid you mean sscanf? You could for example parse a number into a double variable by using "sscanf(str, "%d", &x)" You can even parse a whole line of a csv file with multiple numbers in one call "sscanf(str, "%d;%d;%d\n, &d1, &d2, &d3)"
- criddell 2y agoNo, the article lists sprintf/snprintf. Another person already correctly pointed out that the author was listing a bunch of functions for number and string conversions in general.
- randomNumber7 2y agoprintf makes no sense in this context, because the conversion happens in the other direction. The article doesn't change that. It also lists scanf...
- murderfs 2y agoIt's not a list of ways to convert strings to numbers, it's a list of string conversion functions (i.e. including the other direction). to_string is also listed there.
- vaylian 2y agoExample 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?
- trealira 2y ago&value would be a pointer to that integer. Instead, it's using references, which also use the & character. References use different syntax, but they're like pointers that can't be reassigned. Examples: int x = 0; int &r = x; r += 1; assert(x == 1); int y; r = y; // won't compile void inc(int &r) { r += 1; } int x = 0; inc(x); assert(x == 1); The equivalent using pointers, like in C: int x = 0; int *p = &x; *p += 1; assert(x == 1); int y; p = &y; void inc(int *p) { *p += 1; } int x = 0; inc(&x); assert(x == 1);
- trealira 2y agoMistake: r = y; // won't compile This will compile. It will be effectively the same as "x = y". The pointer equivalent is *p = y". My apologies, as it's been a while since I've used C++.
- hardlianotion 2y agoIn the function signature, value is a reference. You can think of reference as being a pointer that points to a fixed location but with value semantics. So you can dip value into the function call and the function can assign to it, as it could to data pointed to by a pointer.
- _gabe_ 2y agoI saw another commenter explain that it’s passed by reference, but I agree with you. The C++ Core Guidelines even mention that it’s better to use raw pointers (or pass by value and return a value) in cases like this to make the intent clear. https://isocpp.org/wiki/faq/references#call-by-reference https://isocpp.org/wiki/faq/references#call-by-reference