10

The classic preprocessor version of the min function looks like

#define min(a, b) ((a) < (b) ? (a) : (b))

This leaves you open to double evaluation—the situation in which you do min(f(), g()), forgetting that f or g has side effects, and you have to spend hours trying to figure out why your function is running twice. To prevent this, you can do

#define min(a, b) ({__typeof__(a) _a = (a); \
    __typeof__(b) _b = (b); \
    _a < _b ? _a : _b;})

This works great under GCC, but if you run it through Clang with -Wgnu set—this set of warnings falls under the umbrella of -pedantic—you get errors like

test.c:5:10: error: use of GNU statement expression extension [-Werror,-Wgnu]
        int a = min(3, 7);
                ^
test.c:1:22: note: expanded from macro 'min'
#  define min(a, b) ({__typeof__(a) _a = (a); __typeof__(b) _b = (b); _a < _b ? _a : _b;})

Is it possible to define these macros in a way that prevents double evaluation and which is acceptable to Clang under -pedantic? (Yes, you can disable the warning with -Wno-gnu, in which case Clang handles the statement expression without a problem. I’m asking because I, like clang -pedantic, am too picky for my own good.)

Edit: I am working in C. I tagged this C++ too because I thought a solution might apply to C++ as well as to C. Oops—forgot about templates! Sorry for the ambiguity.

bdesham
  • 15,430
  • 13
  • 79
  • 123
  • 5
    I am curious why both *C* and *C++* tags, which language are you targetting? The answer is different depending on the language. – Shafik Yaghmour Dec 18 '13 at 15:19
  • 7
    Simply don't use the preprocessor for it, period. An inline function does the job better and in a more obvious way. And I think in C++ you can even use templates to avoid creating multiple functions for different types. – ThiefMaster Dec 18 '13 at 15:19
  • C or C++. C++ use templates. – RedX Dec 18 '13 at 15:19
  • @ShafikYaghmour based on the compiler error he's targeting C – Mgetz Dec 18 '13 at 15:23
  • @ThiefMaster: But an inline function in C cannot be generic and neither can it be overloaded by type. Which is why people continue to be tempted to use macros for this kind of thing, to avoid writing `min_int`, `min_unsigned_int`, `min_double` etc. – Steve Jessop Dec 18 '13 at 15:37
  • `...and you have to spend hours trying to figure out why your function is running twice`. Exactly! Congratulations, you empirically proved one of the core arguments against using function-like macros to be right: macros are a complete mess to debug and maintain. Now... how long does it take you to write `MIN` as a proper, bug-free, working function? 10 seconds? 20? – Lundin Dec 18 '13 at 15:58
  • @Lundin: It's non-trivial in C. – Lightness Races in Orbit Dec 18 '13 at 16:09
  • I think you could get quite a good solution in C with `_Generic`. – Simple Dec 18 '13 at 16:17
  • 1
    Why is a C++ answer accepted for this question, where you clearly state that you want C and a C version exists? – Jens Gustedt Dec 18 '13 at 16:43
  • @JensGustedt The accepted answer provided a link to a long discussion that concluded with “don’t use the preprocessor for this”. The C answer was less satisfactory to me. – bdesham Dec 18 '13 at 16:58
  • 1
    @bdesham, I don't understand, the link doesn't add much to what you already had in your question. Can you elaborate, please, why the C answer is less satisfactory? As it appears now, this is a C++ answer to a C question, so it will not serve many others. – Jens Gustedt Dec 18 '13 at 17:04
  • @JensGustedt: Because the question did not clearly state that he wanted C at the time that the answer was written, and because a C version does _not_ exist. I say that in my answer, with a reference which is a HUGE previous question on the topic. Please read it. That the answer adds little new to the question is because the OP was _right_, and there's nothing wrong with that. – Lightness Races in Orbit Dec 18 '13 at 17:13
  • what I don't get about that, is that there *is* a C solution for that, now, and it is given in the other answer. The answers in your link dates from 2010 and are kind of obsolete. There is a new version of C since two years now that provides the tools for that. – Jens Gustedt Dec 18 '13 at 17:36
  • @JensGustedt To be honest I was hoping for a terse solution like the “classic version” I mentioned in my question. I already know how to write a `min` function for `int`s, a different one for `long`s, and so on; my point in asking was to find something that didn’t require so much boilerplate. The answer by @Simple was almost entirely boilerplate. – bdesham Dec 18 '13 at 18:11
  • 1
    @bdesham it is boilerplate that you only have to write once though. – Simple Dec 18 '13 at 18:15

3 Answers3

13

If you're really writing in C++, simply don't use the preprocessor:

template <typename T>
const T min(const T& a, const T& b)
{
   return (b < a) ? b : a;
}

(Note that I've swapped b and a, so that you get the left-hand operand if the two are equal.)

Otherwise no, not really.

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • I'd prefer `return (b – BoBTFish Dec 18 '13 at 15:23
  • There is no reason whatsoever to use the preprocessor for this, C or C++ doesn't matter. I'm curious why you would write a function `min` for this in C++ though. Why not overload the `<` operator? It is the very same thing, and this function here will force you to overload `<` anyhow, in case you pass objects to it. – Lundin Dec 18 '13 at 16:06
  • 5
    @Lundin: Because the `<` operator does not pick one of two operands. It gives you a boolean. Show us your genius C solution. – Lightness Races in Orbit Dec 18 '13 at 16:08
  • 1
    Here is an article where Andrei Alexandrescu gets very het up about the correct way to write a `min`/`max` function template: http://www.drdobbs.com/generic-min-and-max-redivivus/184403774. – Oliver Charlesworth Dec 18 '13 at 16:13
  • I am using C (see the edit to the question), so I guess my answer is the “no, not really” part :-) – bdesham Dec 18 '13 at 16:38
  • 1
    Even as a C++ answer this isn't a good one, since this doesn't capture the case that `a` and `b` may have different types. – Jens Gustedt Dec 18 '13 at 17:06
  • @Jens: That's quite deliberate, actually. I choose to enforce that this operation be defined only between two expressions of the _same_ type. It's rare that there's a _good_ way to define `<` between two non-numeric types. You want to convert on the way in? Go right ahead. – Lightness Races in Orbit Dec 18 '13 at 17:11
  • How hard can it be to pick one of the operands from outside the function, if you call the code as `if(a < b) use(a); else use(b);` Seems far more compact and readable to me. Assuming `<` is overloaded `operator <` in C++. – Lundin Dec 18 '13 at 22:36
  • 1
    @bdesham If you are using C then 1) why tag it as C++ and 2) why accept a C++ answer. You don't make any sense. – Lundin Dec 18 '13 at 22:39
  • @Lundin: It is not "a C++ answer". – Lightness Races in Orbit Dec 19 '13 at 10:18
  • 1
    It is written in C++ and will not compile in C. – Lundin Dec 19 '13 at 10:29
  • 1
    @Lundin: I guess you failed to read the _whole_ answer. There are two parts to it. – Lightness Races in Orbit Dec 19 '13 at 10:36
8

I think this would work as a C11 solution.

inline
int min(int const x, int const y)
{
    return y < x ? y : x;
}

inline
unsigned minu(unsigned const x, unsigned const y)
{
    return y < x ? y : x;
}

inline
long minl(long const x, long const y)
{
    return y < x ? y : x;
}

inline
unsigned long minul(unsigned long const x, unsigned long const y)
{
    return y < x ? y : x;
}

inline
long long minll(long long const x, long long const y)
{
    return y < x ? y : x;
}

inline
unsigned long long minull(unsigned long long const x, unsigned long long const y)
{
    return y < x ? y : x;
}

inline
float minf(float const x, float const y)
{
    return y < x ? y : x;
}

inline
double mind(double const x, double const y)
{
    return y < x ? y : x;
}

inline
long double minld(long double const x, long double const y)
{
    return y < x ? y : x;
}

#define MIN(X, Y) (_Generic((X) + (Y),   \
    int:                min,             \
    unsigned:           minu,            \
    long:               minl,            \
    unsigned long:      minul,           \
    long long:          minll,           \
    unsigned long long: minull,          \
    float:              minf,            \
    double:             mind,            \
    long double:        minld)((X), (Y)))
Simple
  • 13,992
  • 2
  • 47
  • 47
  • 2
    +1 but your `_Generic` expression is much too complicated. `_Generic((X)+(Y), int : min, unsigned : minu, ...)((X), (Y))` would suffice. Also you have forgotten the floating point types. – Jens Gustedt Dec 18 '13 at 16:45
  • @JensGustedt thanks. I don't think there's a way to get this to work for pointers though without requiring a `void*` cast. – Simple Dec 18 '13 at 18:09
  • Pointers could be done through a `default` case, only that the choice expression as it is now wouldn't work for pointers :( – Jens Gustedt Dec 18 '13 at 18:28
  • @JensGustedt I think you'd lose the nicer error message then though if you tried to pass a `struct` or something. – Simple Dec 18 '13 at 18:29
4

The C11 solution posted by Simple looks ideal, but in case you don't have a C11 compiler, you could still define a macro with intristic type safety (as type safe as C gets anyhow) :

#define MIN(type, X, Y) min_ ## type(X, Y)

This macro will only allow implemented types to be passed, otherwise you will get a compiler error.

Example:

#define MIN(type, X, Y) min_ ## type(X, Y)

long  min_long  (long x, long y);
char  min_char  (char x, char y);
float min_float (float x, float y);

int main()
{
  long  min_l = MIN (long, 5L, 10L);
  char  min_c = MIN (char, 'A', 'B');
  float min_f = MIN (float, 666.66f, 3.14f);

  printf("%ld\n", min_l);
  printf("%c\n", min_c);
  printf("%f\n", min_f);
}

char  min_char  (char x, char y)    { return x < y ? x : y; }
long  min_long  (long x, long y)    { return x < y ? x : y; }
float min_float (float x, float y)  { return x < y ? x : y; }

Now if you would execute the above macro with MIN(int, 1, 2), you would get a compiler error: "min_int, no such function exists".

Lundin
  • 195,001
  • 40
  • 254
  • 396