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/>).

Attachment: signature.asc
Description: PGP signature

Reply via email to