8 ms·
The MIN Challenge
- thomasahle 12y agoCan anyone explain the advantage of macros like this, rather than just using an `inline function`? Is it because C doesn't have generics?
- TorKlingberg 12y agoYes, C doesn't have generics. In practice most C code uses the fourth version, and you just have to remember not to put things with side effects as macro arguments. #define MIN(a, b) ((a) < (b) ? (a) : (b)) C11 does introduce very limited type-variant macros.
- mitchty 12y agoYep, but for min the C11 generics would work just as well and allow for a bit more flexibility than the older approaches. Though you do have to name mangle manually and they aren't quite as powerful as you'd probably want. http://www.robertgamble.net/2012/01/c11-generic-selections.html http://www.robertgamble.net/2012/01/c11-generic-selections.h...
- orangeduck 12y agoMacros in C can sometimes be nasty, but "min" is a pathological case for a macro because it isn't what macros were meant for. In reality "min" should just be a function, and you should write a separate function for each type you want to define it over. This is how this problem is solved in C, and almost all other languages. It just looks deceptive because C's native operators like "<" and ">" happen to be generic across some number types. This is just for convenience, it isn't how C is meant to be programmed. Pretty much every other language also makes you define different implementations of functions when you have a different type you want it to work on. Lots let you type the same function name, independent of the type - via generics, interfaces, or type-classes - but all call out to different implementations at the end of the day. Very few can derive those implementations. As far as I am concerned, Macros in C are best used just to save on typing. They get a lot of hate because the syntax looks like functions - so people try to use them as functions. I don't recommend using them as functions, instead use them to save typing - because saving typing and avoiding repeating yourself can only reduce errors.
- 14113 12y agoexactly my attitude towards them - modern compilers will generally optimise simple functions like "min" so that they're just as efficient as macro equivalents. For example, for a project that I did, I needed specific access methods for each member of a struct, and for each method to be written with a separate function. With macros, it was as easy as defining a macro to generate said function (given the member name), and then calling said macro for each member to generate the code.
- zatkin 12y agoIn my experience, some compilers will inline functions when it is an optimization (particularly if you declare them static inline).
- Someone 12y ago"min" is a pathological case for a macro because it isn't what macros were meant for. Tell that to Kernighan and Ritchie. The second macro in K&R (I googled a PDF of the second edition) is #define max(A, B) ((A) > (B) ? (A) : (B)) Yes, that's lowercase 'max', and they even mention "This macro will work for any data type; there is no need for different kinds of max for different data types, as there would be with functions" Macros certainly were intended for this, as it was the only way to guarantee that the compiler inlined code (probably the only way it ever inlined function calls) And for the curious: the first macro they give is #define forever for(;;) That certainly is what macros were meant for :-), and doesn't even save on typing. But yes, in modern C, nobody would use a macro for this.
- iano 12y agoNot entirely related to the post, but what's the sane use case for doing: return (expr, expr) like in the first example? I cannot think of a good reason to do this.
- octo_t 12y agoIt simplifies the grammar, immensely.
- deleted 12y ago[deleted]
- kzrdude 12y agoI know this mainly as a useful macro implementation trick if where you want to be able to use it in an expression.
- eatnumber1 12y agoIf the min macro would have worked, it would have allowed the two lines min(1, 2, out); return out; to be condensed into one line.
- tkmcc 12y agoIt may not be a good reason to do this, but you can condense if (failure) { errno = EINVAL; return -1; } into if (failure) return (errno = EINVAL, -1);
- jabagawee 12y agoIn any sane code, you'd have the open and close brackets anyways, so all this code does is trade a few '\n' characters for a few parentheses characters. I'd opt for the former still.
- mihai_ionic 12y agoMeanwhile, in C++14: auto min = [] (auto a, auto b) { return a < b ? a : b; };
- deleted 12y ago[deleted]
- aetherspawn 12y agoMeanwhile, in Haskell: min x y = Data.Bool.bool x y (x > y) Although considering what the article was talking about, doing stuff like this only makes you sound like an ass.
- mihai_ionic 12y agoI have come to really dislike people who think it's a good idea to define macros such as min, max, major and minor in system headers. * http://msdn.microsoft.com/en-us/library/windows/desktop/dd757290%28v=vs.85%29.aspx http://msdn.microsoft.com/en-us/library/windows/desktop/dd75... * http://msdn.microsoft.com/en-us/library/windows/desktop/dd757150%28v=vs.85%29.aspx http://msdn.microsoft.com/en-us/library/windows/desktop/dd75... * https://stackoverflow.com/questions/22240973/major-and-minor-macros-defined-in-sys-sysmacros-h-pulled-in-by-iterator https://stackoverflow.com/questions/22240973/major-and-minor...
- thedigitalengel 12y agoUnfortunately, the solution ultimately proposed doesn't work in this case: int _a = 50; int result = min(num, _a); you end up expanding to "int _a = _a;" which creates a new int _a, and assigns it to itself.
- ridiculous_fish 12y agoThis is a lot more subtle than most programmers realize, thanks to floating point weirdness. An essential property of MIN is that it is symmetric with respect to its arguments: MIN(a, b) and MIN(b, a) are the same thing. But the expression (a < b ? a : b) is not symmetric with NaNs. So what do we do? We could assert that if only one value is an NaN, we return the other value. We might write it like this: if (a != a) return b; return a < b ? a : b; This isn't symmetric if both arguments are different NaNs, but in practice getting different NaNs takes some work. (Note that C99 requires that fmin behave this way with respect to NaN.) But we're still not quite symmetric, thanks to negative zero. Negative zero compares equal to positive zero, but it doesn't seem right that the sign bit of the result should depend on the argument order. glibc doesn't bother to handle negative zero, but Java's Math.min does: public static double min(double a, double b) { if (a != a) return a; if ((a == 0.0d) && (b == 0.0d) && (Double.doubleToLongBits(b) == negativeZeroDoubleBits)) { return b; } return (a <= b) ? a : b; } Delightful. Java has the best min.