When I want to convert between different integer types, it seems the best syntax is to use boost::numeric_cast<>()
:
int y = 99999;
short x
If you're okay with C++17 but don't want your casting algorithm throwing exceptions internally, you can use std::clamp with a bit of wrapping to handle out-of-bounds values.
template
constexpr TTo clamp_cast(const TFrom& src) noexcept
{
using to_limits = std::numeric_limits;
using larger_type = std::conditional_t<(sizeof(TFrom) < sizeof(TTo)), TTo, TFrom>;
if constexpr (std::is_same_v)
{
// don't bother if it is the same type
return src;
}
else if constexpr (std::is_unsigned_v)
{
// if source is unsigned, we only need to worry about upper bound
return TTo(std::min(larger_type(src), larger_type(to_limits::max())));
}
else if constexpr (std::is_unsigned_v)
{
// source is signed, but destination is not
if (src < TFrom(0))
return TTo(0);
else
return TTo(std::min(larger_type(src), larger_type(to_limits::max())));
}
else
{
// both are signed -- use regular clamping rules
return TTo(std::clamp(larger_type(src),
larger_type(to_limits::min()),
larger_type(to_limits::max())
)
);
}
}
Usage is basically what you'd expect:
static_assert(uint16_t(213) == clamp_cast(213));
static_assert(uint16_t(65535) == clamp_cast(9872431));
static_assert(uint16_t(0) == clamp_cast(-98721));