#ifndef XRPL_BASICS_SAFE_CAST_H_INCLUDED #define XRPL_BASICS_SAFE_CAST_H_INCLUDED #include namespace ripple { // safe_cast adds compile-time checks to a static_cast to ensure that // the destination can hold all values of the source. This is particularly // handy when the source or destination is an enumeration type. template concept SafeToCast = (std::is_integral_v && std::is_integral_v) && (std::is_signed::value || std::is_unsigned::value) && (std::is_signed::value != std::is_signed::value ? sizeof(Dest) > sizeof(Src) : sizeof(Dest) >= sizeof(Src)); template inline constexpr std:: enable_if_t && std::is_integral_v, Dest> safe_cast(Src s) noexcept { static_assert( std::is_signed_v || std::is_unsigned_v, "Cannot cast signed to unsigned"); constexpr unsigned not_same = std::is_signed_v != std::is_signed_v; static_assert( sizeof(Dest) >= sizeof(Src) + not_same, "Destination is too small to hold all values of source"); return static_cast(s); } template inline constexpr std:: enable_if_t && std::is_integral_v, Dest> safe_cast(Src s) noexcept { return static_cast(safe_cast>(s)); } template inline constexpr std:: enable_if_t && std::is_enum_v, Dest> safe_cast(Src s) noexcept { return safe_cast(static_cast>(s)); } // unsafe_cast explicitly flags a static_cast as not necessarily able to hold // all values of the source. It includes a compile-time check so that if // underlying types become safe, it can be converted to a safe_cast. template inline constexpr std:: enable_if_t && std::is_integral_v, Dest> unsafe_cast(Src s) noexcept { static_assert( !SafeToCast, "Only unsafe if casting signed to unsigned or " "destination is too small"); return static_cast(s); } template inline constexpr std:: enable_if_t && std::is_integral_v, Dest> unsafe_cast(Src s) noexcept { return static_cast(unsafe_cast>(s)); } template inline constexpr std:: enable_if_t && std::is_enum_v, Dest> unsafe_cast(Src s) noexcept { return unsafe_cast(static_cast>(s)); } } // namespace ripple #endif