From 02390412cfa12c33df1493a887024940ff198f61 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 22 May 2025 19:31:32 -0700 Subject: [PATCH] [type-traits] add `IsUint` and `IsInt` (#11513) This commit adds `TypeTraits::IsUint` and `IsInt` to determine (at compile time) whether a given `Type` is an unsigned or signed integer type (8, 16, 32, or 64 bit length). These help simplify `static_assert()` checks in template methods that work with integer types. --- src/core/common/num_utils.hpp | 68 +++++++++---------------------- src/core/common/serial_number.hpp | 6 +-- src/core/common/tlvs.hpp | 12 ++---- src/core/common/type_traits.hpp | 64 ++++++++++++++++++++++++++++- src/core/net/dns_types.hpp | 6 +-- src/core/net/mdns.cpp | 4 +- 6 files changed, 90 insertions(+), 70 deletions(-) diff --git a/src/core/common/num_utils.hpp b/src/core/common/num_utils.hpp index 18ee2a6dd..9f1df9d1f 100644 --- a/src/core/common/num_utils.hpp +++ b/src/core/common/num_utils.hpp @@ -101,9 +101,7 @@ template Type Clamp(Type aValue, Type aMin, Type aMax) */ template uint8_t ClampToUint8(UintType aValue) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue, - "UintType must be `uint16_t, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); return static_cast(Min(aValue, static_cast(NumericLimits::kMax))); } @@ -141,9 +139,7 @@ template uint16_t ClampToUint16(UintType aValue) */ template int8_t ClampToInt8(IntType aValue) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue, - "IntType must be `int16_t, `int32_t`, or `int64_t`"); + static_assert(TypeTraits::IsInt::kValue, "IntType must be a signed int (8, 16, 32, 64 bit len)"); return static_cast(Clamp(aValue, static_cast(NumericLimits::kMin), static_cast(NumericLimits::kMax))); @@ -250,9 +246,7 @@ inline unsigned long ToUlong(uint32_t aUint32) { return static_cast uint8_t CountBitsInMask(UintType aMask) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, - "UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); uint8_t count = 0; @@ -276,9 +270,7 @@ template uint8_t CountBitsInMask(UintType aMask) */ template void SetBit(UintType &aBits, uint8_t aBitOffset) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, - "UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); aBits = aBits | (static_cast(1) << aBitOffset); } @@ -294,9 +286,7 @@ template void SetBit(UintType &aBits, uint8_t aBitOffset) */ template void ClearBit(UintType &aBits, uint8_t aBitOffset) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, - "UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); aBits = aBits & (~(static_cast(1) << aBitOffset)); } @@ -314,9 +304,7 @@ template void ClearBit(UintType &aBits, uint8_t aBitOffset) */ template bool GetBit(UintType aBits, uint8_t aBitOffset) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, - "UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); return (aBits & (static_cast(1) << aBitOffset)) != 0; } @@ -333,9 +321,7 @@ template bool GetBit(UintType aBits, uint8_t aBitOffset) */ template void WriteBit(UintType &aBits, uint8_t aBitOffset, bool aValue) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, - "UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); if (aValue) { @@ -358,9 +344,7 @@ template void WriteBit(UintType &aBits, uint8_t aBitOffset, */ template inline constexpr uint8_t BitOffsetOfMask(UintType aMask) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, - "UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); return (aMask & 0x1) ? 0 : (1 + BitOffsetOfMask(aMask >> 1)); } @@ -378,9 +362,7 @@ template inline constexpr uint8_t BitOffsetOfMask(UintType a template void WriteBits(UintType &aBits, UintType aValue) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, - "UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); aBits = ((aBits & ~kMask) | ((aValue << kOffset) & kMask)); } @@ -400,9 +382,7 @@ void WriteBits(UintType &aBits, UintType aValue) template UintType UpdateBits(UintType aBits, UintType aValue) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, - "UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); return ((aBits & ~kMask) | ((aValue << kOffset) & kMask)); } @@ -421,9 +401,7 @@ UintType UpdateBits(UintType aBits, UintType aValue) template UintType ReadBits(UintType aBits) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, - "UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); return (aBits & kMask) >> kOffset; } @@ -432,7 +410,7 @@ UintType ReadBits(UintType aBits) * Writes the specified bits of the given integer stored in little-endian format to the given value and returns the * updated integer stored in little-endian format. * - * @tparam UintType The value type (MUST be `uint16_t`, `uint32_t`, or `uint64_t`). + * @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`). * @tparam kMask The bit mask (MUST not be 0) to write. The @p kMask must be provided in a shifted form. * @tparam kOffset The bit offset to write. The default @p kOffset is computed from the given @p kMask. * @@ -444,9 +422,7 @@ UintType ReadBits(UintType aBits) template UintType UpdateBitsLittleEndian(UintType aBits, UintType aValue) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue, - "UintType must be `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); return LittleEndian::HostSwap((LittleEndian::HostSwap(aBits) & ~kMask) | ((aValue << kOffset) & kMask)); @@ -456,7 +432,7 @@ UintType UpdateBitsLittleEndian(UintType aBits, UintType aValue) * Writes the specified bits of the given integer stored in big-endian format to the given value and returns the updated * integer stored in big-endian format. * - * @tparam UintType The value type (MUST be `uint16_t`, `uint32_t`, or `uint64_t`). + * @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`). * @tparam kMask The bit mask (MUST not be 0) to write. The @p kMask must be provided in a shifted form. * @tparam kOffset The bit offset to write. The default @p kOffset is computed from the given @p kMask. * @@ -468,9 +444,7 @@ UintType UpdateBitsLittleEndian(UintType aBits, UintType aValue) template UintType UpdateBitsBigEndian(UintType aBits, UintType aValue) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue, - "UintType must be `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); return BigEndian::HostSwap((BigEndian::HostSwap(aBits) & ~kMask) | ((aValue << kOffset) & kMask)); @@ -479,7 +453,7 @@ UintType UpdateBitsBigEndian(UintType aBits, UintType aValue) /** * Read the value of the specified bits of the given integer stored in little-endian format. * - * @tparam UintType The value type (MUST be `uint16_t`, `uint32_t`, or `uint64_t`). + * @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`). * @tparam kMask The bit mask (MUST not be 0) to write. The @p kMask must be provided in a shifted form. * @tparam kOffset The bit offset to write. The default @p kOffset is computed from the given @p kMask. * @@ -490,9 +464,7 @@ UintType UpdateBitsBigEndian(UintType aBits, UintType aValue) template UintType ReadBitsLittleEndian(UintType aBits) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue, - "UintType must be `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); return (LittleEndian::HostSwap(aBits) & kMask) >> kOffset; } @@ -500,7 +472,7 @@ UintType ReadBitsLittleEndian(UintType aBits) /** * Read the value of the specified bits of the given integer stored in big-endian format. * - * @tparam UintType The value type (MUST be `uint16_t`, `uint32_t`, or `uint64_t`). + * @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`). * @tparam kMask The bit mask (MUST not be 0) to write. The @p kMask must be provided in a shifted form. * @tparam kOffset The bit offset to write. The default @p kOffset is computed from the given @p kMask. * @@ -511,9 +483,7 @@ UintType ReadBitsLittleEndian(UintType aBits) template UintType ReadBitsBigEndian(UintType aBits) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue, - "UintType must be `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); return (BigEndian::HostSwap(aBits) & kMask) >> kOffset; } diff --git a/src/core/common/serial_number.hpp b/src/core/common/serial_number.hpp index ad76811af..79854fc15 100644 --- a/src/core/common/serial_number.hpp +++ b/src/core/common/serial_number.hpp @@ -62,10 +62,8 @@ public: */ template static bool IsLess(UintType aFirst, UintType aSecond) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue, - "UintType MUST be an 8, 16, 32, or 64 bit `uint` type"); + static_assert(TypeTraits::IsUint::kValue, + "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); static constexpr UintType kNegativeMask = (NumericLimits::kMax >> 1) + 1; diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp index 298b53275..1098fd818 100644 --- a/src/core/common/tlvs.hpp +++ b/src/core/common/tlvs.hpp @@ -798,9 +798,7 @@ public: template class UintTlvInfo : public TlvInfo { public: - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue, - "UintTlv must be used used with unsigned int value type"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); typedef UintType UintValueType; ///< The TLV Value unsigned int type. }; @@ -818,12 +816,8 @@ template class SimpleTlvInfo : pu { public: static_assert(!TypeTraits::IsPointer::kValue, "TlvValueType must not be a pointer"); - static_assert(!TypeTraits::IsSame::kValue, "SimpleTlv must not use int value type"); - static_assert(!TypeTraits::IsSame::kValue, "SimpleTlv must not use int value type"); - static_assert(!TypeTraits::IsSame::kValue, "SimpleTlv must not use int value type"); - static_assert(!TypeTraits::IsSame::kValue, "SimpleTlv must not use int value type"); - static_assert(!TypeTraits::IsSame::kValue, "SimpleTlv must not use int value type"); - static_assert(!TypeTraits::IsSame::kValue, "SimpleTlv must not use int value type"); + static_assert(!TypeTraits::IsUint::kValue, "SimpleTlv must not use int value type"); + static_assert(!TypeTraits::IsInt::kValue, "SimpleTlv must not use int value type"); typedef TlvValueType ValueType; ///< The TLV Value type. }; diff --git a/src/core/common/type_traits.hpp b/src/core/common/type_traits.hpp index da1f7a9b9..a3342c685 100644 --- a/src/core/common/type_traits.hpp +++ b/src/core/common/type_traits.hpp @@ -79,7 +79,69 @@ template struct IsPointer : public TrueValue { }; -template struct IsPointer : TrueValue +template struct IsPointer : public TrueValue +{ +}; + +/** + * Indicates whether or not a given template `Type` is an unsigned integer type (`uint8_t`, `uint16_t`, `uint32_t`, or + * `uint64_t`). + * + * The `constexpr` expression `IsUint::kValue` would be `true` when the `Type` is an unsigned int, otherwise it + * would be `false`. + * + * @tparam Type A type to check if is an unsigned integer type. + */ +template struct IsUint : public FalseValue +{ +}; + +// Template specializations of the `IsUint` + +template <> struct IsUint : public TrueValue +{ +}; + +template <> struct IsUint : public TrueValue +{ +}; + +template <> struct IsUint : public TrueValue +{ +}; + +template <> struct IsUint : public TrueValue +{ +}; + +/** + * Indicates whether or not a given template `Type` is a signed integer type (`int8_t`, `int16_t`, `int32_t`, or + * `int64_t`). + * + * The `constexpr` expression `IsInt::kValue` would be `true` when the `Type` is a signed int, otherwise it + * would be `false`. + * + * @tparam Type A type to check if is a signed integer type. + */ +template struct IsInt : public FalseValue +{ +}; + +// Template specializations of the `IsInt` + +template <> struct IsInt : public TrueValue +{ +}; + +template <> struct IsInt : public TrueValue +{ +}; + +template <> struct IsInt : public TrueValue +{ +}; + +template <> struct IsInt : public TrueValue { }; diff --git a/src/core/net/dns_types.hpp b/src/core/net/dns_types.hpp index ae87efda7..7e414440a 100644 --- a/src/core/net/dns_types.hpp +++ b/src/core/net/dns_types.hpp @@ -1352,10 +1352,8 @@ public: */ template Error AppendBigEndianUintEntry(const char *aKey, UintType aUintValue) { - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue, - "UintType must be uint8/uint16/uint32/uint64"); + static_assert(TypeTraits::IsUint::kValue, + "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); return AppendEntry(aKey, BigEndian::HostSwap(aUintValue)); } diff --git a/src/core/net/mdns.cpp b/src/core/net/mdns.cpp index c285e72f6..80ad10d5e 100644 --- a/src/core/net/mdns.cpp +++ b/src/core/net/mdns.cpp @@ -597,9 +597,7 @@ template void Core::RecordInfo::UpdateProperty(UintType &aPr // are similar overloads for `Heap::Data` and `Heap::String` and // `AddressArray` property types below. - static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, - "UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`"); + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); if (!mIsPresent || (aProperty != aValue)) {