From a57fe7676c4205c2a2865274de11098147b84e0d Mon Sep 17 00:00:00 2001 From: Milan Kriz Date: Mon, 9 Sep 2024 13:50:22 +0200 Subject: [PATCH] [#2] Implement type wrappers for bool and floating point types --- runtime/src/zserio/BitSizeOfCalculator.cpp | 4 +- runtime/src/zserio/Types.h | 53 ++++- runtime/test/zserio/TypesTest.cpp | 262 +++++++++++++-------- 3 files changed, 209 insertions(+), 110 deletions(-) diff --git a/runtime/src/zserio/BitSizeOfCalculator.cpp b/runtime/src/zserio/BitSizeOfCalculator.cpp index a6781bf..c377eb9 100644 --- a/runtime/src/zserio/BitSizeOfCalculator.cpp +++ b/runtime/src/zserio/BitSizeOfCalculator.cpp @@ -7,7 +7,7 @@ namespace zserio { -static const std::array VARIN16_MAX_VALUES = { +static const std::array VARINT16_MAX_VALUES = { (UINT64_C(1) << (6)) - 1, (UINT64_C(1) << (6 + 8)) - 1, }; @@ -116,7 +116,7 @@ static uint64_t convertToAbsValue(T value) size_t bitSizeOfVarInt16(int16_t value) { - return bitSizeOfVarIntImpl(convertToAbsValue(value), VARIN16_MAX_VALUES, "varint16"); + return bitSizeOfVarIntImpl(convertToAbsValue(value), VARINT16_MAX_VALUES, "varint16"); } size_t bitSizeOfVarInt32(int32_t value) diff --git a/runtime/src/zserio/Types.h b/runtime/src/zserio/Types.h index 5dc56b1..d6d5aac 100644 --- a/runtime/src/zserio/Types.h +++ b/runtime/src/zserio/Types.h @@ -43,6 +43,12 @@ class TypeWrapper value_type m_value; }; +class BoolWrapper : public TypeWrapper +{ +public: + using TypeWrapper::TypeWrapper; +}; + template class IntWrapper : public TypeWrapper { @@ -50,8 +56,6 @@ class IntWrapper : public TypeWrapper static_assert((BIT_SIZE + 7) / 8 <= sizeof(VALUE_TYPE), "BitSize doesn't fit to the VALUE_TYPE!"); public: - using value_type = VALUE_TYPE; - using TypeWrapper::TypeWrapper; }; @@ -81,6 +85,20 @@ class VarIntWrapper : public TypeWrapper using TypeWrapper::TypeWrapper; }; +enum class FloatType : uint8_t +{ + FLOAT16, + FLOAT32, + FLOAT64 +}; + +template +class FloatWrapper : public TypeWrapper +{ +public: + using TypeWrapper::TypeWrapper; +}; + template struct needs_range_check {}; @@ -88,6 +106,10 @@ struct needs_range_check template inline constexpr bool needs_range_check_v = needs_range_check::value; +template <> +struct needs_range_check : std::false_type +{}; + template struct needs_range_check> { @@ -105,11 +127,14 @@ struct needs_range_check> static constexpr bool value = VAR_TYPE != VarIntType::VAR; }; +template +struct needs_range_check> : std::false_type +{}; + template struct RangeChecker { - static constexpr void check(typename TYPE_WRAPPER::value_type value) noexcept( - !detail::needs_range_check_v) + static constexpr void check(typename TYPE_WRAPPER::value_type value) { if constexpr (std::is_signed_v) { @@ -166,6 +191,20 @@ struct RangeChecker> } // namespace detail +template <> +struct Limits +{ + static constexpr bool min() noexcept + { + return false; + } + + static constexpr bool max() noexcept + { + return true; + } +}; + template struct Limits> { @@ -360,6 +399,8 @@ constexpr typename TYPE_WRAPPER::value_type checked_cast(TYPE_WRAPPER wrapper, B return wrapper; } +using Bool = detail::BoolWrapper; + using Int8 = detail::IntWrapper; using Int16 = detail::IntWrapper; using Int32 = detail::IntWrapper; @@ -514,6 +555,10 @@ using VarUInt = detail::VarIntWrapper; using VarSize = detail::VarIntWrapper; +using Float16 = detail::FloatWrapper; +using Float32 = detail::FloatWrapper; +using Float64 = detail::FloatWrapper; + } // namespace zserio #endif // ifndef ZSERIO_TYPES_H_INC diff --git a/runtime/test/zserio/TypesTest.cpp b/runtime/test/zserio/TypesTest.cpp index fe93c44..6326147 100644 --- a/runtime/test/zserio/TypesTest.cpp +++ b/runtime/test/zserio/TypesTest.cpp @@ -9,23 +9,24 @@ void testSignedStdInt() { using value_type = typename T::value_type; - static_assert(zserio::Limits::min() == std::numeric_limits::min(), "shall be constexpr"); - static_assert(zserio::Limits::max() == std::numeric_limits::max(), "shall be constexpr"); + static_assert(Limits::min() == std::numeric_limits::min(), "shall be constexpr"); + static_assert(Limits::max() == std::numeric_limits::max(), "shall be constexpr"); static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); static_assert(noexcept(checked_cast(std::declval())), "shall be noexcept"); + static_assert(noexcept(checked_cast(std::declval())), "shall be noexcept"); - const T minValue(zserio::Limits::min()); - ASSERT_EQ(zserio::Limits::min(), minValue); + const T minValue(Limits::min()); + ASSERT_EQ(Limits::min(), minValue); - const T maxValue(zserio::Limits::max()); - ASSERT_EQ(zserio::Limits::max(), maxValue); + const T maxValue(Limits::max()); + ASSERT_EQ(Limits::max(), maxValue); - T checkedMin = zserio::checked_cast(zserio::Limits::min()); - ASSERT_EQ(zserio::Limits::min(), checkedMin); + T checkedMin = checked_cast(Limits::min()); + ASSERT_EQ(Limits::min(), checked_cast(checkedMin)); - T checkedMax = zserio::checked_cast(zserio::Limits::max()); - ASSERT_EQ(zserio::Limits::max(), checkedMax); + T checkedMax = checked_cast(Limits::max()); + ASSERT_EQ(Limits::max(), checked_cast(checkedMax)); T value; value = 0; @@ -41,23 +42,24 @@ void testUnsignedStdInt() { using value_type = typename T::value_type; - static_assert(zserio::Limits::min() == std::numeric_limits::min(), "shall be constexpr"); - static_assert(zserio::Limits::max() == std::numeric_limits::max(), "shall be constexpr"); + static_assert(Limits::min() == std::numeric_limits::min(), "shall be constexpr"); + static_assert(Limits::max() == std::numeric_limits::max(), "shall be constexpr"); static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); static_assert(noexcept(checked_cast(std::declval())), "shall be noexcept"); + static_assert(noexcept(checked_cast(std::declval())), "shall be noexcept"); - const T minValue(zserio::Limits::min()); - ASSERT_EQ(zserio::Limits::min(), minValue); + const T minValue(Limits::min()); + ASSERT_EQ(Limits::min(), minValue); - const T maxValue(zserio::Limits::max()); - ASSERT_EQ(zserio::Limits::max(), maxValue); + const T maxValue(Limits::max()); + ASSERT_EQ(Limits::max(), maxValue); - T checkedMin = zserio::checked_cast(zserio::Limits::min()); - ASSERT_EQ(zserio::Limits::min(), checkedMin); + T checkedMin = checked_cast(Limits::min()); + ASSERT_EQ(Limits::min(), checked_cast(checkedMin)); - T checkedMax = zserio::checked_cast(zserio::Limits::max()); - ASSERT_EQ(zserio::Limits::max(), checkedMax); + T checkedMax = checked_cast(Limits::max()); + ASSERT_EQ(Limits::max(), checked_cast(checkedMax)); T value; value = 0; @@ -74,24 +76,24 @@ void testSignedInt() { using value_type = typename T::value_type; - static_assert(zserio::Limits::min() == MIN_VALUE, "shall be constexpr"); - static_assert(zserio::Limits::max() == MAX_VALUE, "shall be constexpr"); + static_assert(Limits::min() == MIN_VALUE, "shall be constexpr"); + static_assert(Limits::max() == MAX_VALUE, "shall be constexpr"); static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); - static_assert(IS_NOEXCEPT == noexcept(checked_cast(std::declval())), "unexpected noexcept"); + static_assert(IS_NOEXCEPT == noexcept(checked_cast(std::declval())), "unexpected noexcept"); - const T minValue(zserio::Limits::min()); - ASSERT_EQ(zserio::Limits::min(), minValue); + const T minValue(Limits::min()); + ASSERT_EQ(Limits::min(), minValue); - const T maxValue(zserio::Limits::max()); - ASSERT_EQ(zserio::Limits::max(), maxValue); + const T maxValue(Limits::max()); + ASSERT_EQ(Limits::max(), maxValue); - T checkedMin = zserio::checked_cast(zserio::Limits::min()); - ASSERT_EQ(zserio::Limits::min(), checkedMin); + T checkedMin = checked_cast(Limits::min()); + ASSERT_EQ(Limits::min(), checked_cast(checkedMin)); - T checkedMax = zserio::checked_cast(zserio::Limits::max()); - ASSERT_EQ(zserio::Limits::max(), checkedMax); + T checkedMax = checked_cast(Limits::max()); + ASSERT_EQ(Limits::max(), checked_cast(checkedMax)); T value; ASSERT_EQ(0, value); @@ -102,12 +104,12 @@ void testSignedInt() if constexpr (!IS_NOEXCEPT) { - ASSERT_THROW(zserio::checked_cast(zserio::Limits::min() - 1), OutOfRangeException); - ASSERT_THROW(zserio::checked_cast(zserio::Limits::max() + 1), OutOfRangeException); + ASSERT_THROW(checked_cast(Limits::min() - 1), OutOfRangeException); + ASSERT_THROW(checked_cast(Limits::max() + 1), OutOfRangeException); const T underMin = minValue - 1; - ASSERT_THROW(zserio::checked_cast(underMin), OutOfRangeException); + ASSERT_THROW(checked_cast(underMin), OutOfRangeException); const T overMax = maxValue + 1; - ASSERT_THROW(zserio::checked_cast(overMax), OutOfRangeException); + ASSERT_THROW(checked_cast(overMax), OutOfRangeException); } } @@ -116,36 +118,37 @@ void testUnsignedInt() { using value_type = typename T::value_type; - static_assert(zserio::Limits::min() == 0, "shall be constexpr"); - static_assert(zserio::Limits::max() == MAX_VALUE, "shall be constexpr"); + static_assert(Limits::min() == 0, "shall be constexpr"); + static_assert(Limits::max() == MAX_VALUE, "shall be constexpr"); static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); static_assert(IS_NOEXCEPT == noexcept(checked_cast(std::declval())), "unexpected noexcept"); + static_assert(IS_NOEXCEPT == noexcept(checked_cast(std::declval())), "unexpected noexcept"); - const T minValue(zserio::Limits::min()); - ASSERT_EQ(zserio::Limits::min(), minValue); + const T minValue(Limits::min()); + ASSERT_EQ(Limits::min(), minValue); - const T maxValue(zserio::Limits::max()); - ASSERT_EQ(zserio::Limits::max(), maxValue); + const T maxValue(Limits::max()); + ASSERT_EQ(Limits::max(), maxValue); - T checkedMin = zserio::checked_cast(zserio::Limits::min()); - ASSERT_EQ(zserio::Limits::min(), checkedMin); + T checkedMin = checked_cast(Limits::min()); + ASSERT_EQ(Limits::min(), checked_cast(checkedMin)); - T checkedMax = zserio::checked_cast(zserio::Limits::max()); - ASSERT_EQ(zserio::Limits::max(), checkedMax); + T checkedMax = checked_cast(Limits::max()); + ASSERT_EQ(Limits::max(), checked_cast(checkedMax)); T value; ASSERT_EQ(0, value); value = 1; ASSERT_EQ(1, value); - value = zserio::Limits::max() - 1; - ASSERT_EQ(zserio::Limits::max() - 1, value); + value = Limits::max() - 1; + ASSERT_EQ(Limits::max() - 1, value); if constexpr (!IS_NOEXCEPT) { - ASSERT_THROW(zserio::checked_cast(zserio::Limits::max() + 1), OutOfRangeException); + ASSERT_THROW(checked_cast(Limits::max() + 1), OutOfRangeException); const T overMax = maxValue + 1; - ASSERT_THROW(zserio::checked_cast(overMax), OutOfRangeException); + ASSERT_THROW(checked_cast(overMax), OutOfRangeException); } } @@ -154,29 +157,30 @@ void testSignedDynInt() { using value_type = typename T::value_type; - static_assert(zserio::Limits::min(8) == -128, "shall be constexpr"); - static_assert(zserio::Limits::max(8) == 127, "shall be constexpr"); + static_assert(Limits::min(8) == -128, "shall be constexpr"); + static_assert(Limits::max(8) == 127, "shall be constexpr"); static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); static_assert(!noexcept(checked_cast(std::declval(), std::declval())), "shall not be noexcept"); + static_assert(!noexcept(checked_cast(std::declval(), std::declval())), "shall not be noexcept"); - ASSERT_THROW(zserio::checked_cast(0, 0), OutOfRangeException); - ASSERT_THROW(zserio::checked_cast(0, sizeof(value_type) * 8 + 1), OutOfRangeException); + ASSERT_THROW(checked_cast(0, 0), OutOfRangeException); + ASSERT_THROW(checked_cast(0, sizeof(value_type) * 8 + 1), OutOfRangeException); for (BitSize numBits = 1; numBits <= sizeof(value_type) * 8; ++numBits) { - const T minValue(zserio::Limits::min(numBits)); - ASSERT_EQ(zserio::Limits::min(numBits), minValue); + const T minValue(Limits::min(numBits)); + ASSERT_EQ(Limits::min(numBits), minValue); - const T maxValue(zserio::Limits::max(numBits)); - ASSERT_EQ(zserio::Limits::max(numBits), maxValue); + const T maxValue(Limits::max(numBits)); + ASSERT_EQ(Limits::max(numBits), maxValue); - T checkedMin = zserio::checked_cast(zserio::Limits::min(numBits), numBits); - ASSERT_EQ(zserio::Limits::min(numBits), checkedMin); + T checkedMin = checked_cast(Limits::min(numBits), numBits); + ASSERT_EQ(Limits::min(numBits), checked_cast(checkedMin, numBits)); - T checkedMax = zserio::checked_cast(zserio::Limits::max(numBits), numBits); - ASSERT_EQ(zserio::Limits::max(numBits), checkedMax); + T checkedMax = checked_cast(Limits::max(numBits), numBits); + ASSERT_EQ(Limits::max(numBits), checked_cast(checkedMax, numBits)); T value; ASSERT_EQ(0, value); @@ -187,15 +191,12 @@ void testSignedDynInt() if (numBits < sizeof(value_type) * 8) { - ASSERT_THROW( - zserio::checked_cast(zserio::Limits::min(numBits) - 1, numBits), OutOfRangeException) - << numBits; - ASSERT_THROW( - zserio::checked_cast(zserio::Limits::max(numBits) + 1, numBits), OutOfRangeException); + ASSERT_THROW(checked_cast(Limits::min(numBits) - 1, numBits), OutOfRangeException) << numBits; + ASSERT_THROW(checked_cast(Limits::max(numBits) + 1, numBits), OutOfRangeException); const T underMin = minValue - 1; - ASSERT_THROW(zserio::checked_cast(underMin, numBits), OutOfRangeException); + ASSERT_THROW(checked_cast(underMin, numBits), OutOfRangeException); const T overMax = maxValue + 1; - ASSERT_THROW(zserio::checked_cast(overMax, numBits), OutOfRangeException); + ASSERT_THROW(checked_cast(overMax, numBits), OutOfRangeException); } } } @@ -205,48 +206,94 @@ void testUnsignedDynInt() { using value_type = typename T::value_type; - static_assert(zserio::Limits::min(8) == 0, "shall be constexpr"); - static_assert(zserio::Limits::max(8) == 255, "shall be constexpr"); + static_assert(Limits::min(8) == 0, "shall be constexpr"); + static_assert(Limits::max(8) == 255, "shall be constexpr"); static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); static_assert(!noexcept(checked_cast(std::declval(), std::declval())), "shall not be noexcept"); + static_assert(!noexcept(checked_cast(std::declval(), std::declval())), "shall not be noexcept"); - ASSERT_THROW(zserio::checked_cast(0, 0), OutOfRangeException); - ASSERT_THROW(zserio::checked_cast(0, sizeof(value_type) * 8 + 1), OutOfRangeException); + ASSERT_THROW(checked_cast(0, 0), OutOfRangeException); + ASSERT_THROW(checked_cast(0, sizeof(value_type) * 8 + 1), OutOfRangeException); for (BitSize numBits = 1; numBits <= sizeof(value_type) * 8; ++numBits) { - const T minValue(zserio::Limits::min(numBits)); - ASSERT_EQ(zserio::Limits::min(numBits), minValue); + const T minValue(Limits::min(numBits)); + ASSERT_EQ(Limits::min(numBits), minValue); - const T maxValue(zserio::Limits::max(numBits)); - ASSERT_EQ(zserio::Limits::max(numBits), maxValue); + const T maxValue(Limits::max(numBits)); + ASSERT_EQ(Limits::max(numBits), maxValue); - T checkedMin = zserio::checked_cast(zserio::Limits::min(numBits), numBits); - ASSERT_EQ(zserio::Limits::min(numBits), checkedMin); + T checkedMin = checked_cast(Limits::min(numBits), numBits); + ASSERT_EQ(Limits::min(numBits), checked_cast(checkedMin, numBits)); - T checkedMax = zserio::checked_cast(zserio::Limits::max(numBits), numBits); - ASSERT_EQ(zserio::Limits::max(numBits), checkedMax); + T checkedMax = checked_cast(Limits::max(numBits), numBits); + ASSERT_EQ(Limits::max(numBits), checked_cast(checkedMax, numBits)); T value; ASSERT_EQ(0, value); value = 1; ASSERT_EQ(1, value); - value = zserio::Limits::max(numBits) - 1; - ASSERT_EQ(zserio::Limits::max(numBits) - 1, value); + value = Limits::max(numBits) - 1; + ASSERT_EQ(Limits::max(numBits) - 1, value); if (numBits < sizeof(value_type) * 8) { - ASSERT_THROW( - zserio::checked_cast(zserio::Limits::max(numBits) + 1, numBits), OutOfRangeException); + ASSERT_THROW(checked_cast(Limits::max(numBits) + 1, numBits), OutOfRangeException); const T overMax = maxValue + 1; - ASSERT_THROW(zserio::checked_cast(overMax, numBits), OutOfRangeException); + ASSERT_THROW(checked_cast(overMax, numBits), OutOfRangeException); } } } -TEST(TypesTest, signedStdInts) +template +void testFloat() +{ + using value_type = typename FLOAT_TYPE::value_type; + + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(noexcept(checked_cast(std::declval())), "shall be noexcept"); + static_assert(noexcept(checked_cast(std::declval())), "shall be noexcept"); + + FLOAT_TYPE value; + ASSERT_DOUBLE_EQ(static_cast(0.0), value); + value = static_cast(1.0); + ASSERT_DOUBLE_EQ(static_cast(1.0), value); + value = static_cast(0.0); + ASSERT_DOUBLE_EQ(static_cast(0.0), value); + + FLOAT_TYPE checked_value = checked_cast(static_cast(1.0)); + ASSERT_DOUBLE_EQ(static_cast(1.0), checked_cast(checked_value)); +} + +TEST(TypesTest, boolType) +{ + static_assert(std::is_nothrow_constructible_v, "shall be noexcept"); + static_assert(noexcept(checked_cast(std::declval())), "shall be noexcept"); + static_assert(noexcept(checked_cast(std::declval())), "shall be noexcept"); + + const Bool minValue(Limits::min()); + ASSERT_EQ(Limits::min(), minValue); + + const Bool maxValue(Limits::max()); + ASSERT_EQ(Limits::max(), maxValue); + + Bool checkedMin = checked_cast(Limits::min()); + ASSERT_EQ(Limits::min(), checked_cast(checkedMin)); + + Bool checkedMax = checked_cast(Limits::max()); + ASSERT_EQ(Limits::max(), checked_cast(checkedMax)); + + Bool value; + ASSERT_EQ(false, value); + value = true; + ASSERT_EQ(true, value); + value = false; + ASSERT_EQ(false, value); +} + +TEST(TypesTest, signedStdIntTypes) { testSignedStdInt(); testSignedStdInt(); @@ -254,7 +301,7 @@ TEST(TypesTest, signedStdInts) testSignedStdInt(); } -TEST(TypesTest, unsignedStdInts) +TEST(TypesTest, unsignedStdIntTypes) { testUnsignedStdInt(); testUnsignedStdInt(); @@ -262,7 +309,7 @@ TEST(TypesTest, unsignedStdInts) testUnsignedStdInt(); } -TEST(TypesTest, signedBitFields) +TEST(TypesTest, signedBitFieldTypes) { testSignedInt(); testSignedInt(); @@ -326,7 +373,7 @@ TEST(TypesTest, signedBitFields) testSignedInt(); } -TEST(TypesTest, unsignedBitFields) +TEST(TypesTest, unsignedBitFieldTypes) { testUnsignedInt(); testUnsignedInt(); @@ -390,7 +437,23 @@ TEST(TypesTest, unsignedBitFields) testUnsignedInt(); } -TEST(TypesTest, signedVarInts) +TEST(TypesTest, signedDynIntTypes) +{ + testSignedDynInt(); + testSignedDynInt(); + testSignedDynInt(); + testSignedDynInt(); +} + +TEST(TypesTest, unsignedDynIntTypes) +{ + testUnsignedDynInt(); + testUnsignedDynInt(); + testUnsignedDynInt(); + testUnsignedDynInt(); +} + +TEST(TypesTest, signedVarIntTypes) { testSignedInt(); testSignedInt(); @@ -398,7 +461,7 @@ TEST(TypesTest, signedVarInts) testSignedInt(); } -TEST(TypesTest, unsignedVarInts) +TEST(TypesTest, unsignedVarIntTypes) { testUnsignedInt(); testUnsignedInt(); @@ -407,20 +470,11 @@ TEST(TypesTest, unsignedVarInts) testUnsignedInt(); } -TEST(TypesTest, signedDynInts) -{ - testSignedDynInt(); - testSignedDynInt(); - testSignedDynInt(); - testSignedDynInt(); -} - -TEST(TypesTest, unsignedDynInts) +TEST(TypesTest, floatTypes) { - testUnsignedDynInt(); - testUnsignedDynInt(); - testUnsignedDynInt(); - testUnsignedDynInt(); + testFloat(); + testFloat(); + testFloat(); } } // namespace zserio