tech-misc archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: alx-0008 - Standardize strtoi(3) and strtou(3) from NetBSD



Hi Paul,

On Wed, Mar 19, 2025 at 05:39:33PM -0700, Paul Eggert wrote:
> On 3/19/25 14:23, Alejandro Colomar wrote:
> 
> > I think not reporting errors or warnings on saturation needs
> > justification.
> 
> I don't know what "justification" means, but if it means a comment in the
> code I'm not sure I agree. Code where saturation is ordinarily what's wanted
> shouldn't need a comment on each nontrivial line saying "Saturation is OK
> here."

Nah, not a source-code comment.  I think comments are usually evil.

More like you telling me now why you do it that way.  Actually, Bruno
send detailed responses in his last email, and I think that you'd
benefit from range checks, actually.  (See my response to his email.)
<https://lore.kernel.org/liba2i/6oyljvsenypqnrmgjbcwskqpdsag677h2dzay6hvfoosju4224@3j7iczm4d7nw/T/#m38066e6eec63a8906e3cbfea275c9d7940d8df98>

> > Thanks, those changes look good.  BTW, what do you think of using
> > strspn(3) to simplify the c_isspace loop?
> 
> Not worth the trouble. The loop is easier to read and debug than the strspn
> call,

I guess I got used to the niceties of strspn(3) that I find it easier to
read.  It's a matter of taste, so ok.  :)

> which got some minor details wrong and fixing that would complicate
> the strspn code even further.

Do you mean that the implementation of strspn(3) was temporarily broken?
Or that the specification is bad?  I'm curious about it; could you
please clarify?

> > However, would you mind clarifying why you don't diagnose huge values in
> > the two places that you have updated?
> 
> For this particular resource, a limit of ULONG_MAX has the same practical
> effect as a limit of ULONG_MAX + 1. Since the user can't tell the difference
> in behavior, it's fine to implement the larger limit as the smaller one,
> with no diagnostic.

According to Bruno, that limit is later clamped at a much lower value,
so I think that clamping could be moved up to the strtou(3) call.

Of course, that would mean having to implement strtou(3) for now, since
it's non-standard, so keeping it as is is simpler.  I was just trying to
say that if strtou(3) was standard in libc, then you could just use it
and simplify code, while making it more robust.

> A reasonable amount of GNU code works that way.

Ok.

> > 	struct foo {
> > 		long  val;
> > 		int   err;
> > 	}
> > 
> > 	struct foo  ret;
> > 
> > 	ret = f(time_t, ...);
> > 	if (ret.err != 0)
> > 		err(1, "f");
> > 
> > How do I know which variant of struct foo I need?
> 
> I don't understand the question. There's no variant here; "variant" to me
> implies something like a union.
> 
> But to fill in the details: C doesn't have a convenient notation for
> returning multiple values, you do need a struct. One convention is to use a
> struct whose tag is the same as the function. So, something like this in a
> header file somewhere:
> 
>     struct a2i { intmax_t val; ptrdiff_t len; }
>     a2i (char const *str, int base);

How do you get a uintmax_t?  Let's say I'm parsing an unsigned variable.

Also, how do I perform range checks in that call?  I need to specify
min and max limits.

> where LEN is negative for errors, and callers look like this:

How do you know how much has been parsed on error?  That's something
useful from strtoi/u(3).

>     struct a2i r = a2i(stringval, 10);
>     if (r.len < 0 || stringval[r.len])
>       err("a2i", stringval, r.len);
> 
> the "|| stringval[r.len]" is needed only for callers that consider
> nonnumeric suffixes to be an error.

How do you perform range checks with this API?

> This is simpler than the pointers and "restrict"s in the proposed API.

Compare to

	QChar *alt_2(typename T,
	             T *restrict n, QChar *s, int base, T min, T max);


which can be called

	time_t  t;
	char    *end;

	errno = 0;
	end = alt_2(time_t, &t, s, 0, past, future);
	if (errno == ERANGE && t == past)
		goto too_old;
	if (errno == ERANGE && t == future)
		goto too_new;
	if (errno == ENOTSUP)
		goto trailing_test;
	if (errno != 0)
		goto hard_error;

	// All's good here.  Can use 't'.

	...
	return;

trailing_text:
	printf("Trailing text: %s", end);

which gives me for free checks that t is between past and future, and
of course saturation.  It also gives me for free type validation that t
is of type time_t.  It calls strtoi(3) if time_t is a signed type, and
strtou(3) if time_t is an unsigned type.

I can perform all the checks to errno that I want, or I can omit them if
I want.  This is the API I'm working on at the moment, and I don't think
a struct has anything more compelling than that.


Have a lovely night!
Alex

-- 
<https://www.alejandro-colomar.es/>

Attachment: signature.asc
Description: PGP signature



Home | Main Index | Thread Index | Old Index