Implementing min() and max() in Clang without doub

2019-04-05 22:12发布

问题:

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.

回答1:

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.



回答2:

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


回答3:

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".



标签: c clang