I'm trying to come up with a narrowing cast (a general solution) that gracefully ignores lost data. In Visual Studio, a narrowing cast that loses data triggers a "Run-Time Check Failure #1". I do not want to turn it off, instead I'm trying to implement a narrow_cast
that would gracefully do narrowing casts and wouldn't trigger the run time check.
Visual Studio suggests:
char c = (i & 0xFF);
So I started with this, and came up with this ugly solution:
template< typename T >
struct narrow_mask
{
static const T value = T(0xFFFFFFFFFFFFFFFF);
};
template <typename T, typename U>
T narrow_cast(const U& a)
{
return static_cast<T>(a & narrow_mask<T>::value );
}
While it works (VS it seems is perfectly fine with losing data on constants), it's neither complete (no support for non integral data), nor correct (I think it would not work correctly for signed values).
Any suggestions for a better solution, or a better narrow_mask implementation?
Edit: In face of comments that this question is VS specific, I checked the standard document, and it seems that the result of a narrowing static_cast
is implementation dependant. Hence, the question can be nicer stated as creating a well-defined (ergo, not implementation dependant) narrowing cast. I do not care much about the specifics of the result value, as long as it's well defined and dependant on the type (not return 0
).
Use std::numeric_limits
and the modulus operator. Get the maximum allowed value of the destination type, cast it to the source type, add one, take the modulus, and cast to the destination type.
The resulting value will certainly be representable in the destination type, i.e. there will be no undefined behavior, but I don't know if MSVC will still throw a warning. I don't have a copy of it to check.
This doesn't preserve negative numbers, though. It can probably be extended to do so, but I'm not sure how. (It's getting late here.)
template< typename to, typename from >
to narrow_cast( from value ) {
static_assert( std::numeric_limits< to >::max() < std::numeric_limits< from >::max(),
"narrow_cast used in non-narrowing context" );
return static_cast< to >( from %
( static_cast< from >( std::numeric_limits< to >::max() ) + 1 ) ) );
}
Here's one version that uses a little bit of C++11. If you don't have access to constexpr
, you can just delete it. If you don't have access to std::make_unsigned
, it's possible to implement your own. If you don't have std::enable_if
, you might be able to use Boost's (or make your own). It works for both signed and unsigned types, as well as positive and negative values. Update: updated to work with floating point types (and floating point to integral, and vice versa).
#include <type_traits>
// From integer type to integer type
template <typename to, typename from>
constexpr typename std::enable_if<std::is_integral<from>::value && std::is_integral<to>::value, to>::type
narrow_cast(const from& value)
{
return static_cast<to>(value & (static_cast<typename std::make_unsigned<from>::type>(-1)));
}
// For these next 3 versions, you'd be best off locally disabling the compiler warning
// There isn't much magic you can do when floating point types get invovled
// From floating point type to floating point type
template <typename to, typename from>
constexpr typename std::enable_if<std::is_floating_point<from>::value && std::is_floating_point<to>::value, to>::type
narrow_cast(const from& value)
{
// The best you can do here is a direct cast
return static_cast<to>(value);
}
// From integer type to floating point type
template <typename to, typename from>
constexpr typename std::enable_if<std::is_integral<from>::value && std::is_floating_point<to>::value, to>::type
narrow_cast(const from& value)
{
// The best you can do here is a direct cast
return static_cast<to>(value);
}
// From floating point type to integer type
template <typename to, typename from>
constexpr typename std::enable_if<std::is_floating_point<from>::value && std::is_integral<to>::value, to>::type
narrow_cast(const from& value)
{
// The best you can do here is a direct cast
return static_cast<to>(value);
}
Bjarne's TCPPPL comes up with some kind of narrow cast which does a runtime check to see if some conversion loses information. Taken textually from Section 11.5 :
template<class Target, class Source>
Target narrow_cast(Source v)
{
auto r = static_cast<Target>(v);
if (static_cast<Source>(r) != v)
throw runtime_error("narrow_cast<>() failed");
return r;
}
The basic idea is to check if the backward conversion gives back the original value; then we should be satisfied since there was no lost information. This version works fine with integral/integral conversions since they are all well defined (narrowing to signed types must be well defined by the implementation, raising an exception would be non conforming). Although, you should probably refine this definition with other overloads and put some SFINAE since this code could lead to UB in the cases of floating/floating and integral/floating conversions when the target type is too small, even for unsigned types.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With