Unfortunately, I have to perform a narrowing cast when calling a 3rd party library. I don't want to impose an overhead in my release build, so will be using a static_cast. However, it's an array index so could lead to some entertainment if it ends up negative. Is there some way to create a safe cast in debug mode only that will check the value to ensure there is no loss during the cast? The only way I can think of is to use macros, and I'd rather not.
For example, in both release and debug mode using MSVC:
int main() {
long long ll = std::numeric_limits<long>::max();
++ll;
std::cout << ll << "\n";
long l = static_cast<long>(ll);
std::cout << l << "\n";
}
results in the output:
2147483648
-2147483648
Using a macro:
template<class to, class from>
to checked_static_cast(const from& from_value) {
to to_value = static_cast<to>(from_value);
if (static_cast<from>(to_value) != from_value)
throw std::runtime_error("Naughty cast");
return to_value;
}
#ifdef _DEBUG
#define debug_checked_static_cast(to, val) checked_static_cast<to>(val)
#else
#define debug_checked_static_cast(to, val) static_cast<to>(val)
#endif
int main() {
try {
long long ll = std::numeric_limits<long>::max();
++ll;
std::cout << ll << "\n";
long l = debug_checked_static_cast(long, ll);
std::cout << l << "\n";
} catch (const std::exception& e) {
std::cerr << "ERROR: " << e.what() << "\n";
}
}
results in the same output in release mode, but the following in debug:
2147483648
ERROR: Naughty cast
Any better options?
NB: I'm disregarding the entertainment we might enjoy from an array large enough to cause this issue, and perhaps this is simply over-paranoid, but I imagine the concept may have applications other than my specific requirement.
No macro needed, you can simply use preprocessor conditionals inside the function body:
template<class to, class from>
inline to debug_checked_static_cast(const from& from_value) {
to to_value{static_cast<to>(from_value)};
#if _DEBUG
from round_trip{to_value};
if (round_trip != from_value)
throw std::runtime_error("Naughty cast");
#endif
return to_value;
}
and
template<class to, class from>
inline to debug_checked_coercion(const from& from_value) {
to to_value{from_value};
#if _DEBUG
from round_trip{to_value};
if (round_trip != from_value)
throw std::runtime_error("Naughty cast");
#endif
return to_value;
}
Then use
long l = debug_checked_coercion<long>(ll);
Notice that I've minimized the use of static_cast
, since it isn't needed for narrowing numeric conversions.
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