Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best strategy to get rid of "warning C4267 possible loss of data"?

I ported some legacy code from win32 to win64. Not because the win32 object size was too small for our needs, but just because win64 is more standard now and we wish to port all our environments to this format (and we also use some 3rd party libs offering better performance in 64bits than in 32bits).

We end up with tons of;

warning C4267: 'argument': conversion from 'size_t' to '...', possible loss of data

Mainly due to code like: unsigned int size = v.size(); where v is a STL container.

I know why the warning makes sense, I know why it is issued and how it could be fixed. However, in this specific example, we never experienced cases where the container size exceeded unsigned int's max value in the past.... so there will be no reason for this problem to appear when code is ported to 64bits environment.

We had discussions on what would be the best strategy to supress those noisy warnings (they may hide a relevant one we will miss), but we could not make a decision on the apropriate strategy.

So I'm asking the question here, what would be the best recommended strategy?

1. Use a static_cast

Use a static_cast. Do unsigned int size = static_cast<unsigned int>(v.size());. I don't "like" that because we loose the 64bits capability to store a huge amount of data in a container. But as our code never reached the 32bits limit, so this appears to be a safe solution...

2. Replace unsigned int by size_t

That's definitely harder as unsigned int size object in the example above could be pased to other functions, saved as class attribute and then removing a one-line warning could end up in doing hundreds of code change...

3. Disable the warning

That's most likely a very bad idea as it would also disable warning in this case uint8_t size = v.size() which is definitely likely to cause loss of data....

4. Define a "safe cast"* function and use it

Something like:

template <typename From, typename To> To safe_cast( const From& value )
{
    //assert( value < std::numeric_limits<To>::max() && value > std::numeric_limits<To>::min() );
    // Edit 19/05: test above fails in some unsigned to signed cast (int64_t to uint32_t), test below is better:
    assert(value == static_cast<From>(static_cast<To>(value))); // verify we don't loose information!
    // or throw....
    return static_cast<To>( value ); 
}

5. Other solutions are welcome...

"Use solution 1 in this case but 2 in this case" could perfectly be a good answer.

like image 766
jpo38 Avatar asked Apr 25 '16 07:04

jpo38


1 Answers

Use the correct type (option 2) - the function/interface defines that type for you, use it.

std::size_t size = v.size(); // given vector<>::size_type is size_t
// or a more verbose
decltype(v)::size_type size = v.size();

It goes to the intent... you are getting the size of v and that size has a type. If the correct type had been used from the beginning, this would not have been a problem.

If you require that value later as another type, transform it then; the safe_cast<> is then a good alternative that includes the runtime bounds checking.

Option 6. Use auto

When you use size = v.size(), if you are not concerned what the type is, only that you use the correct type,

auto size = v.size();

And let the compiler do the hard work for you.

like image 170
Niall Avatar answered Oct 12 '22 15:10

Niall