5 ms·
Sure, you can't get away from handling the cases, but as the article clearly demonstrates, there can be a much better interface for it.
by xigoi 5y ago
Sure, you can't get away from handling the cases, but as the article clearly demonstrates, there can be a much better interface for it.
- kazinator 5y agoI'm not convinced. Can the Rust function which is shown (that function alone) tokenize a number out of a string such that the number is overflowing the target type? Yet indicate to the caller where that overflowing number ends, so that tokenization can continue with subsequent characters, if any? E.g. suppose we have a string with this kind of syntax: "12345 : 12345 , 12345" We can 1. use strtol to get the first integer and a pointer to just after it. 2. use ptr += strspn(ptr, " ") to skip spaces 3. check for the colon and if we find it, skip with ptr++ 4. use strtol to get the second integer (possibly preceded by space). 5. similarly to the colon handling, do the comma 6. strtol again to get the integer. This is efficient: no splitting of the string into pieces requiring memory allocation, and extra list processing. We can code this robustly: it can recognize valid syntax even if some of the numbers overflow. So for this kind of input: "1234523442345345345234534545454545 : 12345 12345" the code could diagnose the overflow, and the missing comma in one pass. If you don't care about the details, just "is this number in range, with no trailing junk, or else is it bad", then raw strtol isn't convenient. But takes only a little code to wrap it.
- kaba0 5y agoRust can return string slices, so a tuple of a return type with a potential number and a slice for the still unprocessed string would be an option, which is much safer than your proposed one and arguably more readable.
- iovrthoughtthis 5y agobut this is not a appropriate place for that functionality.
- kazinator 5y agoIt is entirely appropriate for a function which lexically analyzes a buffer in memory in order to match an integer to be able to tell you where that integer ends.
- iovrthoughtthis 5y agoi think thats a reasonable opinion in the context of say, language implementation. for the relatively simple case of parsing cli arguments i would want an equally simple api. "is this string a valid representation of a number?" and "what number does this string represent?" should be separate apis. even in language implementation, i'd want the identification of number tokens to be separate from the parsing of those number tokens. i would then have another, separate api for "where does the first number end in this string?" which would probably more likely be "return to me the next substring from this string that represents a number"
- xigoi 5y agoHow often do you actually need this?
- tempodox 5y agoWhenever you do lexical analysis on syntax containing numbers. On today's hand-held supercomputers avoiding allocations and, generally, exercising memory-efficiency may not be a primary concern, but it very much was at the time when this stuff was built. And it's still relevant today on restricted systems, like microcontrollers, where C is still the primary language.
- lifthrasiir 5y ago> Whenever you do lexical analysis on syntax containing numbers. If that was an intention C should have a full set of lexical analysis functions, but it doesn't (scanf doesn't count). strtol being able to distinguish two error cases and thus being marginally useful for lexical analysis is most likely accidental.
- xigoi 5y agoIn my experience, reading a single number is a much much much more common operation than doing lexical analysis.
- foxfluff 5y ago> Sure, you can't get away from handling the cases, but as the article clearly demonstrates, there can be a much better interface for it. It's a very apples to oranges comparison, to the point that it almost feels like a straw man. "Interface (that does X) sucks for doing Y; look at how easy the Rust interface for doing Y is!" Yes, there can be a much simpler interface for the case when you want to assert that a string is nothing but digits and must fully convert. That's not what strtol is for though. Now I think libc sucks (no surprise given its age; complaining about is beating a dead horse), and it sucks more if you don't take various GNU & BSD extensions with it, but I'm kinda getting tired of people complaining that "foo in C is hard" when their unstated requirement is that they can't use any libraries to help them do it. Like this fellow the other day: https://news.ycombinator.com/item?id=29990897 https://news.ycombinator.com/item?id=29990897 If you look at programs written in "modern" languages, they almost invariably bring a plethora of libraries and dependencies with them anyway so why is C repeatedly judged on the merits of ancient libc interfaces that you don't have to use?
- xigoi 5y agoIMO, external libraries are for domain-specific tasks. If something is needed in pretty much every program, it should be a part of the language or the standard library. Also, it's much easier to use external libraries in other languages. npm install, cargo install, nimble install, cabal install, gem install, …
- foxfluff 5y ago> If something is needed in pretty much every program, it should be a part of the language or the standard library. It sure would be convenient that way. That said, you don't need to convert strings in pretty much every program. There's a lot of C code out there that does very little with strings. Now do you dismiss an entire language if its standard library is lacking or doesn't exist? IMO that would be throwing out baby with the bathwater. > npm install, cargo install, nimble install, cabal install, gem install, … Yes, I've witnessed the mountain of unaudited dependencies that somehow turn a 300 line program into something the size of my kernel.. should I dismiss all those languages because people do something I don't like with their libraries?
- dfawcus 5y agoOften one can ignore the errno case, as the input is (by then) semi-constrained, and its ambiguity resolution is not needed. e.g. so an idomatic pattern from some real code would be something like: static bool parse_thing (char *value, struct thing *th) { char *endp = NULL; unsigned long firstport = strtoul(value, &endp, 10); if (endp == value || *endp || firstport > 0xffff) { /* Do something with error, like log it */ return false; } th->firstport = firstport; return true; } But granted, for the general case I'd prefer to use some helper like either of: bool parse_decimal_uint32(struct string const *str, uint32_t *outval); bool parse_decimal_uint32(char const *str, unsigned slen, uint32_t *outval);
- kazinator 5y agoIf you can ignore the errno case due to a "semi-constrainted" input, and don't care about trailing junk or having a pointer to more string material after the number is scanned, you can just call atol(str).