Warning conversion from size_t to int in x64

Started by
30 comments, last by swiftcoder 8 years, 2 months ago

Someone calls List.IndexOf(x), but x is not in the list. You're the IndexOf function. What do you return?

Representing error codes in the same space as normal answers doesn't strike me as a particularly good thing to do. You can return a pair of a bool and a size_t. You make the bool false if x is not in the list.

Limiting the size to UInt32MaxValue-1 doesn't seem as bad as wasting an entire half of the space for error messages.

I sometimes use signed integers for things that are essentially indices, so perhaps size_t would be more natural. But then I don't get to do this:


  for (int n = 7; n>=0; --n) ...
Advertisement

for (int n = 7; n>=0; --n) ...


Just write it like this, lol:

for ( unsigned n = 7; n <= 7; --n )

Problem solved?

I'm always confused why people find size_t being unsigned to be a problem. Why are you performing signed comparisons on array indices in the first place? There are very few valid use-cases thereof.


Someone calls List<T>.IndexOf(x), but x is not in the list. You're the IndexOf function. What do you return?

signed -> return -1.

unsigned -> return Length+1 (essentially vector::end or equivalent)? What if the length is UInt32.MaxValue? You don't want to return 0. You either need to limit lists to UInt32.MaxValue-1 in length so that you can use MaxValue (i.e. (uint)-1) as the invalid position, or have two return values - Index and IsValid.

iterator (or some other custom type) -> collection_type::end, or a (index, isvalid) tuple, but this means your API now burdens people with understanding the mechanics of iterators or unpacking return values from complex types.


In my opinion, the API simplicity of signed values outweighs the loss of array sizes over 2 billion elements long. If you want collections larger than that, you can still make a custom type using ulong indices and complex IndexOf return values when you need it.

This is a problem of poor interface design for the container, not a problem with unsigned types. I would prefer something like this using an output parameter for the index:


bool IndexOf( const T& x, size_t& index ) const;

Then the usage of the method is:


size_t index;
if ( container.IndexOf( x, index ) )
{
… use index
}


for (int n = 7; n>=0; --n) ...

Just write it like this, lol:

for ( unsigned n = 7; n <= 7; --n )
Problem solved?

Yes, I've done that kind of thing before, but two things about it make me feel "dirty":

(1) The condition `n <= 7' doesn't express intent very well.

(2) The code wouldn't work if `unsigned' were a 3-bit type (i.e, it doesn't work if n is as large as it can be according to its type).

I mean, technically what you are writing is bugs, but I won't quibble smile.png

Fair enough!

I'm always confused why people find size_t being unsigned to be a problem. Why are you performing signed comparisons on array indices in the first place? There are very few valid use-cases thereof.

It could be argued that the compiler could be stricter, and treat all sign mismatches in comparisons as errors. But it could also be argued that if you aren't developing with -Wall -Werror, then you are inviting this sort of thing...

I'm not. I came across this while working with unsigned matrix indices, I think. That was quite some time ago, but I believe everyone has tripped over the unsigned < 0 condition in a loop at least once in their programming career.

I thought "casting size_t to int => warning" was the whole point of this threadlaugh.png

Representing error codes in the same space as normal answers doesn't strike me as a particularly good thing to do. You can return a pair of a bool and a size_t. You make the bool false if x is not in the list.

I don't think there is something inherently wrong with returning an invalid index vs returning an after-the-end iterator or throwing an exception. Which brings us back to the exception vs error code discussion from last week smile.png

The warning is very likely coming from returns from size_t funtions being caught in ints. The correct response is to hunt it down and fix it in each case, but in all honesty you're probably only going to get erroneous behaviour one in a million times from this.

This is a horrible thing to say, but... If it's not critical then just silence the warning in these builds.

If it is critical then fix it and send PRs to the authors, since this is indeed a bug.
void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.


This is a problem of poor interface design for the container, not a problem with unsigned types. I would prefer something like this using an output parameter for the index:
bool IndexOf( const T& x, size_t& index ) const;
Then the usage of the method is:
size_t index;
if ( container.IndexOf( x, index ) )
{
… use index
}

In general I agree with "don't conflate error codes with values", but when it comes to array indices I'm not as convinced (I mean, even the C++ standard conflates valid iterators with errors).

The pattern above becomes cumbersome in the common case when you know IndexOf will succeed.

This is a problem of poor interface design for the container, not a problem with unsigned types. I would prefer something like this using an output parameter for the index:





bool IndexOf( const T& x, size_t& index ) const;
Then the usage of the method is:


size_t index;
if ( container.IndexOf( x, index ) )
{
… use index
}

I like that approach as well. .Net uses that for "bool Dictionary<>.TryGetValue(key, out value)".

Last time I used C++ (before any of the C++11 or newer stuff), it didn't have the "out" semantics. Just "ref". I.e. the compiler can't tell whether the IndexOf function actually set the index to a value, or just left it uninitialized.

C# will prevent you from EVER using an uninitialized value, which I like a lot.

Do the modern versions of C++ have anything like this yet?

It's not really a matter of "yet". It's just not in keeping with the C++ philosophy.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.


The pattern above becomes cumbersome in the common case when you know IndexOf will succeed.

I mean, honestly, if you know it will succeed, then you don't need *either* method of propagating errors, and what you really want is a IndexOf(std::no_error_result) API?

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

This topic is closed to new replies.

Advertisement