ericw-tools/include/common/numeric_cast.hh

133 lines
4.5 KiB
C++

/* Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
See file, 'COPYING', for details.
*/
#pragma once
#include <limits>
#include <stdexcept>
template<typename Dst, typename Src>
constexpr bool numeric_cast_will_overflow(const Src &value)
{
using DstLim = std::numeric_limits<Dst>;
using SrcLim = std::numeric_limits<Src>;
constexpr bool positive_overflow_possible = DstLim::max() < SrcLim::max();
constexpr bool negative_overflow_possible = SrcLim::is_signed || (DstLim::lowest() > SrcLim::lowest());
// unsigned <-- unsigned
if constexpr ((!DstLim::is_signed) && (!SrcLim::is_signed)) {
if constexpr (positive_overflow_possible) {
if (value > DstLim::max()) {
return true;
}
}
}
// unsigned <-- signed
else if constexpr ((!DstLim::is_signed) && SrcLim::is_signed) {
if constexpr (positive_overflow_possible) {
if (value > DstLim::max()) {
return true;
}
}
if constexpr (negative_overflow_possible) {
if (value < 0) {
return true;
}
}
}
// signed <-- unsigned
else if constexpr (DstLim::is_signed && (!SrcLim::is_signed)) {
if constexpr (positive_overflow_possible) {
if (value > DstLim::max()) {
return true;
}
}
}
// signed <-- signed
else if constexpr (DstLim::is_signed && SrcLim::is_signed) {
if constexpr (positive_overflow_possible) {
if (value > DstLim::max()) {
return true;
}
}
if constexpr (negative_overflow_possible) {
if (value < DstLim::lowest()) {
return true;
}
}
}
return false;
}
template<typename Dst, typename Src>
constexpr Dst numeric_cast(const Src &value, const char *overflow_message = "value")
{
if (numeric_cast_will_overflow<Dst, Src>(value)) {
throw std::overflow_error(overflow_message);
}
return static_cast<Dst>(value);
}
// helper functions to quickly numerically cast mins/maxs
// and floor/ceil them in the case of float -> integral
template<typename T, typename F>
inline qvec<T, 3> aabb_mins_cast(const qvec<F, 3> &f, const char *overflow_message = "mins")
{
if constexpr (std::is_floating_point_v<F> && !std::is_floating_point_v<T>)
return {numeric_cast<T>(floor(f[0]), overflow_message), numeric_cast<T>(floor(f[1]), overflow_message),
numeric_cast<T>(floor(f[2]), overflow_message)};
else
return {numeric_cast<T>(f[0], overflow_message), numeric_cast<T>(f[1], overflow_message),
numeric_cast<T>(f[2], overflow_message)};
}
template<typename T, typename F>
inline qvec<T, 3> aabb_maxs_cast(const qvec<F, 3> &f, const char *overflow_message = "maxs")
{
if constexpr (std::is_floating_point_v<F> && !std::is_floating_point_v<T>)
return {numeric_cast<T>(ceil(f[0]), overflow_message), numeric_cast<T>(ceil(f[1]), overflow_message),
numeric_cast<T>(ceil(f[2]), overflow_message)};
else
return {numeric_cast<T>(f[0], overflow_message), numeric_cast<T>(f[1], overflow_message),
numeric_cast<T>(f[2], overflow_message)};
}
// shortcut template to trim (& convert) std::arrays
// between two lengths
template<typename ADest, typename ASrc>
constexpr ADest array_cast(const ASrc &src, const char *overflow_message = "src")
{
ADest dest{};
for (size_t i = 0; i < std::min(dest.size(), src.size()); i++) {
if constexpr (std::is_arithmetic_v<typename ADest::value_type> &&
std::is_arithmetic_v<typename ASrc::value_type>)
dest[i] = numeric_cast<typename ADest::value_type>(src[i], overflow_message);
else
dest[i] = static_cast<typename ADest::value_type>(src[i]);
}
return dest;
}