[HN Gopher] Ratatoi is a C libary that wraps stdlib's strtol (as...
___________________________________________________________________
Ratatoi is a C libary that wraps stdlib's strtol (as atoi does),
but it's evil.
Author : rept0id-2
Score : 24 points
Date : 2025-05-21 19:00 UTC (4 hours ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| rept0id-2 wrote:
| Ratatoi is a C libary that wraps stdlib's strtol (as atoi does),
| but it's evil.
|
| If an overflow is detected, it calls abort(), you crash and get
| Aborted (core dumped).
|
| This way, you prioritize memory safety over silently running in a
| wrong state, without needing to call strtol and check errors
| manually everywhere.
| timewizard wrote:
| "This way, you open yourself up to DDoS attacks, instead of just
| handling your own errors correctly."
| kstrauser wrote:
| I prefer this approach. It's kind of like using `.expect()` or
| `.unwrap()` in Rust when there's no plausible way the call
| should fail. Like if my program writes out a JSON file and then
| reads it back in, and the file isn't value JSON, panicking is a
| reasonable way to deal with the situation. Someone the world
| got itself into a strange state I can't reasonably recover
| from.
|
| Well, same here. If you're using strtol on data that must be
| well-formed, and it's not, and you're at an earlier stage of
| startup like parsing the config file, go ahead and blow up.
| That's almost certainly better than plowing ahead with invalid
| data.
| LukeShu wrote:
| > Like if my program writes out a JSON file and then reads it
| back in, and the file isn't value JSON, panicking is a
| reasonable way to deal with the situation.
|
| Like how if I hit ctrl-C at just the right moment during the
| build, the next time I build, cmake will segfault and I have
| to delete the build directory.
| threeducks wrote:
| I think strtol is just a badly designed function. The return
| value should have been an error code and the actual long should
| have been "returned" via a pointer. Checking the return value is
| much easier than checking endptr and errno and remembering to set
| errno before calling strtol.
|
| The fact that the strtol example in the manual is 50 lines long,
| of which most is error handling, speaks for itself.
| https://man7.org/linux/man-pages/man3/strtol.3.html#:~:text=...
|
| That being said, I can't imagine many applications where crashing
| is a good solution.
| wahern wrote:
| OpenBSD added strtonum to <stdlib.h>. It's quite opinionated,
| but fits the usage patterns OpenBSD developers prefer.
|
| > 50 lines long, of which most is error handling
|
| That's a little exaggerated considering the example is an
| entire C program, including main. It's more like 14 lines,
| ignoring the '\0' check (which isn't necessary if you don't
| want to parse additional items), and even that includes
| whitespace and stderr logging.
|
| I agree the biggest headache is reliance on errno and the
| nuanced--albeit conventional, consistent[1], and documented--
| semantics of only setting errno on error. Some POSIX APIs, at
| least those defined from scratch (e.g. pthreads, as opposed to
| incorporated common extensions) prefer returning the error code
| directly as you recommend. But this would technically only save
| you a single line, though it might save alot of confusion about
| semantics.
|
| However, in this case I might keep returning the value directly
| and take an error pointer, similar to OpenBSD's strtonum.
| Otherwise you would need separate routines for char, short,
| int, long, and long long. Though there's still the issue of
| unsigned integers, and using _Generic it might be possible to
| at least hide all the type-specific variants behind a single
| interface.
|
| And there's still the issue of needing to check for trailing
| garbage, or dropping the ability to use the interface inline
| with other parsing code. There's alot of dimensions to the
| problem. Parsing integers may be conceptually simple[2], but
| designing an interface that's easy to _integrate_ into
| applications across a variety of contexts is much less simple.
|
| [1] Consistent across libc interfaces, excepting those that may
| invoke syscalls internally, like printf, where errno may be
| incidentally modified even on success.
|
| [2] I personally often prefer to just write a simple loop to
| manually parse integers. It's sometimes easier to integrate the
| desired error checking inline, or even elide it altogether
| (garbage-in, garbage-out).
| gpderetta wrote:
| C can actually return structs by value and small structs are
| actually handled quite efficiently in some ABIs, so a pair
| result/error would be quite convenient although I guess not
| idiomatic.
| PaulDavisThe1st wrote:
| > The return value should have been an error code and the
| actual long should have been "returned" via a pointer.
|
| Oh, you mean like: int ret = sscanf (str,
| "%d", &value); ?
| sltkr wrote:
| Yes, that API is actually great, but the problem with
| (s)scanf() is that reading an invalid value is undefined
| behavior. So you can't use it if you don't already know the
| result fits in &value, which is exactly the situation where
| you'd use strtol() instead.
| duneroadrunner wrote:
| So I don't write much C code these days, but I recently
| encountered strtol() again and am I mistaken or does the
| interface also violate const correctness? I mean it takes a
| _const char*_ as the first parameter and then gives you back a
| (non- _const_ ) _char*_ potentially pointing into the same
| string, right? Like, does strtol() get a pass because it 's
| old, or is const correctness (still) not generally a concern of
| C programmers?
| ksherlock wrote:
| Your errno checks aren't correct.
|
| errno is only set on an error. It's not cleared on success. If
| errno was previously set, the function will always abort(). So
| you need to do something like: int saved_errno
| = errno; errno = 0; .... errno =
| saved_errno; return aInt;
| ben0x539 wrote:
| Doesn't it set errno to 0 first thing?
| ksherlock wrote:
| ahh, you're right. It's still polite to save and restore
| though :)
| snarfy wrote:
| pun driven development
| cmovq wrote:
| Interestingly, a complete implementation of strtol [1] is shorter
| than this wrapper. If you don't like strtol's API or error
| handling, just implement your own.
|
| [1]: https://github.com/gcc-
| mirror/gcc/blob/master/libiberty/strt...
|
| > If an overflow is detected, it calls abort()
|
| An aside, but this doesn't detect overflows on Windows due to
| both long and int being 32 bits (you'd want strtoll for that).
___________________________________________________________________
(page generated 2025-05-21 23:01 UTC)