Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ static_cast with assert

Tags:

c++

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.

like image 464
Rai Avatar asked May 23 '14 13:05

Rai


1 Answers

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.

like image 127
Ben Voigt Avatar answered Oct 16 '22 14:10

Ben Voigt