News for hackers. Stuff that matters.
Re:Maximal Min() and Max() by wakamoleguy on Thursday August 08, 2024 @11:24AM (#41192521)
Why are these defined as macros at all? A function call would come with overhead, of course, but wouldn't compilers be able to inline that anyways?
Re:Maximal Min() and Max() by lifthrasiir on Thursday August 08, 2024 @11:24AM (#41192527)
C doesn't have any type-generic function declaration. So any viable solution had to be at least partially powered by a macro and resulting (one-directional) type inference.
Re:Maximal Min() and Max() by kevin_thibedeau on Thursday August 08, 2024 @12:41PM (#41193482)
C11 does with _Generic but it is limited in ways that make min/max implementations flaky.
Re:Maximal Min() and Max() by pyth0 on Thursday August 08, 2024 @01:03PM (#41193759)
Even with _Generic you can't declare generic functions. You'd still need a macro call that uses _Generic to dispatch different implementations depending on the parameter types.
Re:Maximal Min() and Max() by dwattttt on Thursday August 08, 2024 @05:32PM (#41196542)
Generic dispatching all the type combinations (or warning or erroring) wouldn't be a problem in a project the scale of the Linux kernel.

I'm unfamiliar with the incantations needed to try preserve constant expressions though, that might be too much for them.

Re:Maximal Min() and Max() by lifthrasiir on Thursday August 08, 2024 @09:33PM (#41198066)
The concrete problem that the kernel is facing is an exponential growth of macro expansion. Any current solution, safe or not in terms of re-evaluation, needs at least two copies of both arguments to be expanded [1], so expressions like `min(min(a, b), c)` will expand some arguments four times. The growth factor would be much larger than two if the definition wasn't carefully designed to avoid such cases.

[1] `_Generic` also requires at least two because you need one copy to select the generic implementation and another to call it. C23 `typeof` (or the equivalent GNU extension) allows for a compact type tuple matching:

    inline static int max_int(int x, int y) { return x > y ? x : y; }
    inline static unsigned max_uint(unsigned x, unsigned y) { return x > y ? x : y; }
    /* ... */

    #define MAX(a, b) (_Generic( \
        (void (*)(typeof(a), typeof(b))) NULL, \
        void (*)(int, int): max_int, \
        void (*)(unsigned, unsigned): max_uint, \
        /* ... */ \
        default: max_type_error \
    ) (a, b))
Re:Maximal Min() and Max() by kelnos on Thursday August 08, 2024 @11:30PM (#41198556)
Also _Generic is intended for a function that takes a single varying type. The kernel's min/max macros allow you to safely mix types.
Re:Maximal Min() and Max() by ufo on Thursday August 08, 2024 @11:36AM (#41192671)
Another issue is that some of these macros are intended to be used in a cobatant context, such as array dimensions.
Re:Maximal Min() and Max() by LeifCarrotson on Thursday August 08, 2024 @12:23PM (#41193245)
*constant
Re:Maximal Min() and Max() by olliej on Thursday August 08, 2024 @07:29PM (#41197485)
The problem is C lacks an equivalent to C++’s constexpr (or preceding template insanity), so you can’t use functions in constant contexts, eg

    struct S {
      int foo[max(10, 15)];
    }
Isn’t possible in C, macros are the only option.

So even if you were to try to use _Generic in a macro to handle type correct dispatch you would not be able to use that macro in many of the contexts it is needed.

Honestly constexpr is something that would really help C, and does not need to bring in any other c++ features. Although I guess in this case the lack of the full template and such features set would mean matching the kernel requirements would still require a macro+_Generic to adopt.