TLW
TLW

Reputation: 1439

Casting typedef'd numeric type to size_t catching overflows

I have an unknown typedef'd numeric type. I'd like to cast it to a size_t if possible (read: if it fits within the range of a size_t), otherwise falling through to some error handling code.

Is there a portable way to do so?

Effectively what I'd like to do is:

some_int_type val = <blah>;
if (val < 0 || val > SIZE_MAX) {
    // handle error
} else {
    return (size_t) val;
}

However, this doesn't work, as val may be signed.

My second thought would be to do this:

if (val < 0 || (unsigned_some_int_type) val > SIZE_MAX) {
    // handle error
} else {
    return (size_t) val;
}

This would work (I think) - except that I don't have the unsigned version of some_int_type. I could cast them both to some_int_type, except that that would not work properly if some_int_type is smaller than size_t (and wouldn't work properly if some_int_type is signed, either, now that I think about it). I could cast them both to uintmax_t - except that some compilers actually have integer types larger than uintmax_t. (Notably, __[u]int128).

So how could I go about doing this?

Upvotes: 0

Views: 102

Answers (3)

chux
chux

Reputation: 153456

OP's original code should be fine

some_int_type val = <blah>;
if (val < 0 || val > SIZE_MAX) {
    // handle error
} else {
    return (size_t) val;
}

OP commented to @Olaf "error: comparison between signed and unsigned integer expressions". Either OP truly saw a warning or has warnings being treated as errors. IAC, it is not a error per C.

If OP is unable to ignore the warning/error, then gently convert to size_t only as needed. If the unknown type is wider than size_t, multiplying it by (size_t)1 will not change type and the compare warning should not occur. IAC, the value of val will not change.

#include <stdint.h>
if (val < 0 || (size_t)1 * val > SIZE_MAX) {

Let the compiler optimize it. Only lesser compilers, could use (size_t)0 + val.


Further: the message "comparison between signed and unsigned integer expressions" comes up only in select signed integer and unsigned integer compares. It does not come up when the unsigned integer fits in the range of the signed integer type as there would be no problem converting the unsigned integer type to the wider signed integer type.

Upvotes: 1

dbush
dbush

Reputation: 223872

After checking the negative case, check if the size of the type in question is bigger than a size_t. If so, cast SIZE_MAX to type some_int_type and do the comparison. If not, the value can't be larger than SIZE_MAX.

if (val < 0) {
    // handle error
} else if ((sizeof(some_int_type) > sizeof(size_t)) && (val > (some_int_type)SIZE_MAX)) {
    // handle error
} else {
    return (size_t) val;
}

Upvotes: 0

infixed
infixed

Reputation: 1155

Worry about the lack of an unsigned version by pulling it inside the test for positive

if (val >= 0 )
{
    if (val <=  SIZE_MAX)
    {
        return (size_t) val;
    }
}

// Handle error
return 0;

Upvotes: 0

Related Questions