Hi Michael, On Wed, Oct 15, 2025 at 02:47:46PM +0200, Michael Matz wrote: > Hello, > > On Tue, 14 Oct 2025, Alejandro Colomar wrote: > > > Because bool is entirely different from the other regular integer types, > > and has entirely different rules, > > Why do you say that? bool and its operations is a normal finite algebra > on an ordered set, the only thing being that its add/mul are saturating > and have "funny" colloquial names (namely "or" and "and"): > > add == or ; mul == and
Saturating arithmetics are what make it different, IMO.
> Because the set is finite and fully ordered comparison makes (a) sense,
> and the (b) max/min exists; and because it is so very small these two
> operations actually coincide with add and mul as well: max == or and
> min == and. The maxof/minof of that type is again trivial as well, and
> arguably even more elegantly defined than on the other integer types
> (because of the saturating behaviour of add, that maxof is the fixed-point
> of the operation add1, and minof the fixed point of mul-any).
I agree it would make perfect sense to define _Maxof and _Minof with
bool. It is clear that _Maxof(bool)==true and _Minof(bool)==false;
I don't think anyone would be surprised by that. However, I doubt the
usefulness of that.
Here are all the uses of these operators in the shadow utils project
(of course, implemented as macros with the usual bit shifts):
alx@devuan:~/src/shadow/shadow/master$ grep -rh -C2 'type_m' lib* src/
| grep -v ^$
#define str2i(T, ...) a2i(T, __VA_ARGS__, NULL, 0, type_min(T),
type_max(T))
#define str2sh(...) str2i(short, __VA_ARGS__)
--
get_gid(const char *restrict gidstr, gid_t *restrict gid)
{
return a2i(gid_t, gid, gidstr, NULL, 10, type_min(gid_t),
type_max(gid_t));
}
--
get_pid(const char *restrict pidstr, pid_t *restrict pid)
{
return a2i(pid_t, pid, pidstr, NULL, 10, 1, type_max(pid_t));
}
--
get_uid(const char *restrict uidstr, uid_t *restrict uid)
{
return a2i(uid_t, uid, uidstr, NULL, 10, type_min(uid_t),
type_max(uid_t));
}
--
} else {
if (a2i(rlim_t, &l, value, NULL, 10, 0,
type_max(rlim_t)) == -1
&& errno != ENOTSUP)
{
--
#define stype_max(T)
\
(
\
(T) (((((T) 1 << (WIDTHOF(T) - 2)) - 1) << 1) + 1)
\
)
#define utype_max(T)
\
(
\
(T) -1
\
)
#define type_max(T)
\
(
\
(T) (is_signed(T) ? stype_max(T) : utype_max(T))
\
)
#define type_min(T)
\
(
\
(T) ~type_max(T)
\
)
--
const char *pos;
struct id_range result = {
.first = type_max(id_t),
.last = type_min(id_t)
};
--
static_assert(is_same_type(id_t, gid_t), "");
first = type_min(id_t);
last = type_max(id_t);
if (a2i(id_t, &first, str, &pos, 10, first, last) == -1
All of the uses are actually related to a2i() calls. a2i() is
a type-generic macro wrapper around strtoi(3bsd) --which itself is a
wrapper around strtoimax(3)--, which works for arbitrary (non-boolean)
integer types.
Another case where I would expect these macros to be useful is in the
implementation of <stdbit.h> APIs. Do we want to support things like
popcount((bool)1)? The answers are coupled, I think.
For example:
T
bit_ceil(T x)
{
return 1 + (_Maxof(T) >> leading_zeros(x));
}
> > Also, it's easy to extend features, but not so much to narrow them.
> > Thus, I find it better (safer) to exclude it, at least initially. I
> > welcome anyone interested in supporting bool to show a valid use case
> > for it. I prefer to be cautious by default.
>
> Exceptions are the enemy of good language design. max/min on bool are
> well defined as are maxof/minof. Excluding it from these operators seems
> fairly unnatural. My 2 cents :-)
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).
signature.asc
Description: PGP signature
