2

How to re-write code to implement the same test, but portably avoid the warning?


AFAIK, INT_MAX and SIZE_MAX are not defined to one always being >= than the other, hence the use for the following function to detect problems converting from int to size_t.

#include <assert.h>
#include <stddef.h>
#include <stdint.h>

size_t int_to_size_t(int size) {
  assert(size >= 0);

  #pragma GCC diagnostic ignored "-Wtype-limits"

  // Without the above pragma, below line of code may cause:
  // "warning: comparison is always true due to limited range of data type
  // [-Wtype-limits]"
  assert((unsigned)size <= SIZE_MAX);

  #pragma GCC diagnostic warning "-Wtype-limits"

  return (size_t) size;
}

Different compilers use various mechanisms to squelch warnings. I looking for a portable solution.

The above soluton is not portable and this gcc method unfortunately has a side-effect: Warning -Wtype-limits, which may or may not have been enabled is now enabled after this code. Do not know how to restore -Wtype-limits setting.

Ref:
Portability of #warning preprocessor directive
Suppress comparison always true warning for Macros?

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256

2 Answers2

8

You could replace this:

assert((unsigned)size <= SIZE_MAX);

by:

#if INT_MAX > SIZE_MAX
assert((unsigned)size <= SIZE_MAX);
#endif

If the #if condition is false, the assert condition is always true and the assert is unnecessary. The (unsigned) cast is (probably) necessary to avoid a warning about a comparison between signed and unsigned operands.

Warning: I have not tested this. (To fully test it, I'd need access to a system with int wider than size_t, and I've never seen such a system.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
3

As Keith states correctly, you'd only have to worry when the value of INT_MAX is greater than the one for SIZE_MAX. If you want to compare these values in the preprocessor (and you should) you can't cast to make INT_MAX an unsigned type. (The preprocessor doesn't know about casts.)

Therefore you have to add 0U to the value so both expression become uintmax_t. (For the preprocessor integer constants are intmax_t or uintmax_t).

#if (INT_MAX+0U) > SIZE_MAX
assert(size <= (int)SIZE_MAX);
#endif

Then inside this #if you know that SIZE_MAX is less that INT_MAX so you may cast it down to int without changing the value.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • 1
    I don't think the `+0U` is necessary. As far as the preprocessor is concerned, `INT_MAX` is of type `intmax_t`, and `SIZE_MAX` is of type `uintmax_t`. The `<` operator converts the `intmax_t` operand to `uintmax_t` and then compares the two operands. – Keith Thompson Mar 06 '14 at 22:25
  • @KeithThompson, none of the casts is "necessary". There are good rules on how signed types are converted to unsigned ones, there is no real problem, here. The problem are compilers that are overprotective, and the pseudo-cast in the preprocessor part could avoid that. – Jens Gustedt Mar 06 '14 at 22:33
  • Ok, the cast may avoid a warning -- but experiments shows that gcc, at least doesn't warn about signed vs. unsigned comparisons in preprocessor expressions. Perhaps other compilers are pickier. – Keith Thompson Mar 06 '14 at 22:43