From faae2514b901da924d45eb210cc4649225accb38 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Wed, 3 Sep 2025 15:58:19 -0400 Subject: [PATCH] Rewrite Units.h and same safe_cast.h restrictions to use concepts --- include/xrpl/basics/safe_cast.h | 7 ++- include/xrpl/protocol/Units.h | 92 +++++++++++---------------------- 2 files changed, 34 insertions(+), 65 deletions(-) diff --git a/include/xrpl/basics/safe_cast.h b/include/xrpl/basics/safe_cast.h index f193f1793f..a750a3b6fc 100644 --- a/include/xrpl/basics/safe_cast.h +++ b/include/xrpl/basics/safe_cast.h @@ -28,9 +28,8 @@ namespace ripple { // the destination can hold all values of the source. This is particularly // handy when the source or destination is an enumeration type. -template -static constexpr bool is_safetocasttovalue_v = - (std::is_integral_v && std::is_integral_v) && +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) @@ -78,7 +77,7 @@ inline constexpr std:: unsafe_cast(Src s) noexcept { static_assert( - !is_safetocasttovalue_v, + !SafeToCast, "Only unsafe if casting signed to unsigned or " "destination is too small"); return static_cast(s); diff --git a/include/xrpl/protocol/Units.h b/include/xrpl/protocol/Units.h index d6085891e6..9459f5cb82 100644 --- a/include/xrpl/protocol/Units.h +++ b/include/xrpl/protocol/Units.h @@ -50,25 +50,32 @@ struct unitlessTag; class BipsTag; class TenthBipsTag; -template -using enable_if_unit_t = typename std::enable_if_t< - std::is_class_v && std::is_object_v && - std::is_object_v>; +// These names don't have to be too descriptive, because we're in the "unit" +// namespace. -/** `is_usable_unit_v` is checked to ensure that only values with +template +concept Valid = std::is_class_v && std::is_object_v && + std::is_object_v; + +/** `Usable` is checked to ensure that only values with known valid type tags can be used (sometimes transparently) in non-unit contexts. At the time of implementation, this includes all known tags, but more may be added in the future, and they should not be added automatically unless determined to be appropriate. */ -template > -constexpr bool is_usable_unit_v = - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v; +template +concept Usable = Valid && + (std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v); + +template +concept Compatible = Valid && std::is_arithmetic_v && + std::is_arithmetic_v && + std::is_convertible_v; template class ValueUnit : private boost::totally_ordered>, @@ -85,25 +92,6 @@ public: private: value_type value_; -protected: - template - static constexpr bool is_compatible_v = - std::is_arithmetic_v && std::is_arithmetic_v && - std::is_convertible_v; - - template > - static constexpr bool is_compatiblevalue_v = - is_compatible_v && - std::is_same_v; - - template - using enable_if_compatible_t = - typename std::enable_if_t>; - - template - using enable_if_compatiblevalue_t = - typename std::enable_if_t>; - public: ValueUnit() = default; constexpr ValueUnit(ValueUnit const& other) = default; @@ -135,12 +123,9 @@ public: /** Instances with the same unit, and a type that is "safe" to convert to this one can be converted implicitly */ - template < - class Other, - class = std::enable_if_t< - is_compatible_v && - is_safetocasttovalue_v>> + template Other> constexpr ValueUnit(ValueUnit const& value) + requires SafeToCast : ValueUnit(safe_cast(value.value())) { } @@ -255,7 +240,7 @@ public: return value_ == other.value_; } - template > + template Other> constexpr bool operator==(ValueUnit const& other) const { @@ -268,7 +253,7 @@ public: return value_ == other; } - template > + template Other> constexpr bool operator!=(ValueUnit const& other) const { @@ -310,12 +295,13 @@ public: return static_cast(value_) / reference.value(); } - // `is_usable_unit_v` is checked to ensure that only values with + // `Usable` is checked to ensure that only values with // known valid type tags can be converted to JSON. At the time // of implementation, that includes all known tags, but more may // be added in the future. - std::enable_if_t, Json::Value> + Json::Value jsonClipped() const + requires Usable { if constexpr (std::is_integral_v) { @@ -372,43 +358,27 @@ to_string(ValueUnit const& amount) return std::to_string(amount.value()); } -template > +template constexpr bool can_muldiv_source_v = std::is_convertible_v; -template > +template constexpr bool can_muldiv_dest_v = can_muldiv_source_v && // Dest is also a source std::is_convertible_v && sizeof(typename Dest::value_type) >= sizeof(std::uint64_t); -template < - class Source1, - class Source2, - class = enable_if_unit_t, - class = enable_if_unit_t> +template constexpr bool can_muldiv_sources_v = can_muldiv_source_v && can_muldiv_source_v && std::is_same_v; -template < - class Source1, - class Source2, - class Dest, - class = enable_if_unit_t, - class = enable_if_unit_t, - class = enable_if_unit_t> +template constexpr bool can_muldiv_v = can_muldiv_sources_v && can_muldiv_dest_v; // Source and Dest can be the same by default -template < - class Source1, - class Source2, - class Dest, - class = enable_if_unit_t, - class = enable_if_unit_t, - class = enable_if_unit_t> +template constexpr bool can_muldiv_commute_v = can_muldiv_v && !std::is_same_v;