Where are MIN
and MAX
defined in C, if at all?
What is the best way to implement these, as generically and type safely as possible? (Compil
Looks like Windef.h
(a la #include <windows.h>
) has max
and min
(lower case) macros, that also suffer from the "double evaluation" difficulty, but they're there for those that don't want to re-roll their own :)
Avoid non-standard compiler extensions and implement it as a completely type-safe macro in pure standard C (ISO 9899:2011).
Solution
#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))
#define ENSURE_int(i) _Generic((i), int: (i))
#define ENSURE_float(f) _Generic((f), float: (f))
#define MAX(type, x, y) \
(type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
Usage
MAX(int, 2, 3)
Explanation
The macro MAX creates another macro based on the type
parameter. This control macro, if implemented for the given type, is used to check that both parameters are of the correct type. If the type
is not supported, there will be a compiler error.
If either x or y is not of the correct type, there will be a compiler error in the ENSURE_
macros. More such macros can be added if more types are supported. I've assumed that only arithmetic types (integers, floats, pointers etc) will be used and not structs or arrays etc.
If all types are correct, the GENERIC_MAX macro will be called. Extra parenthesis are needed around each macro parameter, as the usual standard precaution when writing C macros.
Then there's the usual problems with implicit type promotions in C. The ?:
operator balances the 2nd and 3rd operand against each other. For example, the result of GENERIC_MAX(my_char1, my_char2)
would be an int
. To prevent the macro from doing such potentially dangerous type promotions, a final type cast to the intended type was used.
Rationale
We want both parameters to the macro to be of the same type. If one of them is of a different type, the macro is no longer type safe, because an operator like ?:
will yield implicit type promotions. And because it does, we also always need to cast the final result back to the intended type as explained above.
A macro with just one parameter could have been written in a much simpler way. But with 2 or more parameters, there is a need to include an extra type parameter. Because something like this is unfortunately impossible:
// this won't work
#define MAX(x, y) \
_Generic((x), \
int: GENERIC_MAX(x, ENSURE_int(y)) \
float: GENERIC_MAX(x, ENSURE_float(y)) \
)
The problem is that if the above macro is called as MAX(1, 2)
with two int
, it will still try to macro-expand all possible scenarios of the _Generic
association list. So the ENSURE_float
macro will get expanded too, even though it isn't relevant for int
. And since that macro intentionally only contains the float
type, the code won't compile.
To solve this, I created the macro name during the pre-processor phase instead, with the ## operator, so that no macro gets accidentally expanded.
Examples
#include <stdio.h>
#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))
#define ENSURE_int(i) _Generic((i), int: (i))
#define ENSURE_float(f) _Generic((f), float: (f))
#define MAX(type, x, y) \
(type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
int main (void)
{
int ia = 1, ib = 2;
float fa = 3.0f, fb = 4.0f;
double da = 5.0, db = 6.0;
printf("%d\n", MAX(int, ia, ib)); // ok
printf("%f\n", MAX(float, fa, fb)); // ok
//printf("%d\n", MAX(int, ia, fa)); compiler error, one of the types is wrong
//printf("%f\n", MAX(float, fa, ib)); compiler error, one of the types is wrong
//printf("%f\n", MAX(double, fa, fb)); compiler error, the specified type is wrong
//printf("%f\n", MAX(float, da, db)); compiler error, one of the types is wrong
//printf("%d\n", MAX(unsigned int, ia, ib)); // wont get away with this either
//printf("%d\n", MAX(int32_t, ia, ib)); // wont get away with this either
return 0;
}
It's worth pointing out I think that if you define min
and max
with the ternary operation such as
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
then to get the same result for the special case of fmin(-0.0,0.0)
and fmax(-0.0,0.0)
you need to swap the arguments
fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)
If you need min/max in order to avoid an expensive branch, you shouldn't use the ternary operator, as it will compile down to a jump. The link below describes a useful method for implementing a min/max function without branching.
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
The simplest way is to define it as a global function in a .h
file, and call it whenever you want, if your program is modular with lots of files. If not, double MIN(a,b){return (a<b?a:b)}
is the simplest way.
Where are
MIN
andMAX
defined in C, if at all?
They aren't.
What is the best way to implement these, as generically and type safe as possible (compiler extensions/builtins for mainstream compilers preferred).
As functions. I wouldn't use macros like #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
, especially if you plan to deploy your code. Either write your own, use something like standard fmax or fmin, or fix the macro using GCC's typeof (you get typesafety bonus too) in a GCC statement expression:
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
Everyone says "oh I know about double evaluation, it's no problem" and a few months down the road, you'll be debugging the silliest problems for hours on end.
Note the use of __typeof__
instead of typeof
:
If you are writing a header file that must work when included in ISO C programs, write
__typeof__
instead oftypeof
.