Skip to content

Commit

Permalink
[#2] Implement type wrappers for fixed size integral types
Browse files Browse the repository at this point in the history
  • Loading branch information
Mi-La committed Sep 6, 2024
1 parent 82542c9 commit 288dffb
Show file tree
Hide file tree
Showing 7 changed files with 548 additions and 0 deletions.
3 changes: 3 additions & 0 deletions runtime/ClangTidySuppressions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ google-explicit-constructor:src/zserio/Span.h:138
google-explicit-constructor:src/zserio/Span.h:150
google-explicit-constructor:src/zserio/Span.h:163
google-explicit-constructor:src/zserio/Span.h:176
# This is requirement on type wrappers in Zserio C++17 extension
google-explicit-constructor:src/zserio/Types.h:64
google-explicit-constructor:src/zserio/Types.h:78

# False positive, this is a template method.
modernize-use-equals-default:src/zserio/Span.h:80
Expand Down
1 change: 1 addition & 0 deletions runtime/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ set(ZSERIO_CPP17_RUNTIME_LIB_SRCS
zserio/FloatUtil.cpp
zserio/FloatUtil.h
zserio/HashCodeUtil.h
zserio/OutOfRangeException.h
zserio/RebindAlloc.h
zserio/RuntimeArch.h
zserio/SizeConvertUtil.cpp
Expand Down
20 changes: 20 additions & 0 deletions runtime/src/zserio/OutOfRangeException.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef ZSERIO_OUT_OF_RANGE_EXCEPTION_H_INC
#define ZSERIO_OUT_OF_RANGE_EXCEPTION_H_INC

#include "zserio/CppRuntimeException.h"

namespace zserio
{

/**
* Exception thrown when a value is out of range.
*/
class OutOfRangeException : public CppRuntimeException
{
public:
using CppRuntimeException::CppRuntimeException;
};

} // namespace zserio

#endif // ifndef ZSERIO_OUT_OF_RANGE_EXCEPTION_H_INC
240 changes: 240 additions & 0 deletions runtime/src/zserio/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,245 @@
#define ZSERIO_TYPES_H_INC

#include <cstdint>
#include <limits>
#include <type_traits>

#include "zserio/OutOfRangeException.h"

namespace zserio
{

/** Typedef for bit size type used in Zserio. */
using BitSize = unsigned int;

namespace detail
{

template <typename VALUE_TYPE, BitSize BIT_SIZE = 8 * sizeof(VALUE_TYPE)>
class IntegralType
{
private:
static_assert((BIT_SIZE + 7) / 8 <= sizeof(VALUE_TYPE), "BitSize doesn't fit the the VALUE_TYPE!");

static constexpr bool IS_FULL_STD_INTEGRAL_TYPE = sizeof(VALUE_TYPE) * 8 == BIT_SIZE &&
(BIT_SIZE == 8 || BIT_SIZE == 16 || BIT_SIZE == 32 || BIT_SIZE == 64);

public:
using value_type = VALUE_TYPE;
static constexpr value_type MIN_VALUE = []() {
if constexpr (IS_FULL_STD_INTEGRAL_TYPE)
{
return std::numeric_limits<value_type>::min();
}
else if constexpr (std::is_signed_v<value_type>)
{
return -static_cast<value_type>(1ULL << (BIT_SIZE - 1U));
}
else
{
return value_type();
}
}();

static constexpr value_type MAX_VALUE = []() {
if constexpr (IS_FULL_STD_INTEGRAL_TYPE)
{
return std::numeric_limits<value_type>::max();
}
else if constexpr (std::is_signed_v<value_type>)
{
return static_cast<value_type>((1ULL << (BIT_SIZE - 1U)) - 1U);
}
else
{
return static_cast<value_type>((1ULL << BIT_SIZE) - 1U);
}
}();

constexpr explicit IntegralType() noexcept :
m_value()
{}

constexpr IntegralType(value_type value) noexcept(IS_FULL_STD_INTEGRAL_TYPE) :
m_value(withRangeCheck(value))
{}

constexpr value_type get() const
{
return m_value;
}

void set(value_type value) noexcept(IS_FULL_STD_INTEGRAL_TYPE)
{
m_value = withRangeCheck(value);
}

constexpr operator value_type() const
{
return m_value;
}

private:
value_type withRangeCheck(value_type value) const noexcept(IS_FULL_STD_INTEGRAL_TYPE)
{
if constexpr (!IS_FULL_STD_INTEGRAL_TYPE)
{
if constexpr (std::is_signed_v<value_type>)
{
if (value < MIN_VALUE || value > MAX_VALUE)
{
throw OutOfRangeException();
}
}
else
{
if (value > MAX_VALUE)
{
throw OutOfRangeException();
}
}
}

return value;
}

value_type m_value;
};

} // namespace detail

using Int8 = detail::IntegralType<int8_t>;
using Int16 = detail::IntegralType<int16_t>;
using Int32 = detail::IntegralType<int32_t>;
using Int64 = detail::IntegralType<int64_t>;

using UInt8 = detail::IntegralType<uint8_t>;
using UInt16 = detail::IntegralType<uint16_t>;
using UInt32 = detail::IntegralType<uint32_t>;
using UInt64 = detail::IntegralType<uint64_t>;

using Int1 = detail::IntegralType<int8_t, 1>;
using Int2 = detail::IntegralType<int8_t, 2>;
using Int3 = detail::IntegralType<int8_t, 3>;
using Int4 = detail::IntegralType<int8_t, 4>;
using Int5 = detail::IntegralType<int8_t, 5>;
using Int6 = detail::IntegralType<int8_t, 6>;
using Int7 = detail::IntegralType<int8_t, 7>;
using Int9 = detail::IntegralType<int16_t, 9>;
using Int10 = detail::IntegralType<int16_t, 10>;
using Int11 = detail::IntegralType<int16_t, 11>;
using Int12 = detail::IntegralType<int16_t, 12>;
using Int13 = detail::IntegralType<int16_t, 13>;
using Int14 = detail::IntegralType<int16_t, 14>;
using Int15 = detail::IntegralType<int16_t, 15>;
using Int17 = detail::IntegralType<int32_t, 17>;
using Int18 = detail::IntegralType<int32_t, 18>;
using Int19 = detail::IntegralType<int32_t, 19>;
using Int20 = detail::IntegralType<int32_t, 20>;
using Int21 = detail::IntegralType<int32_t, 21>;
using Int22 = detail::IntegralType<int32_t, 22>;
using Int23 = detail::IntegralType<int32_t, 23>;
using Int24 = detail::IntegralType<int32_t, 24>;
using Int25 = detail::IntegralType<int32_t, 25>;
using Int26 = detail::IntegralType<int32_t, 26>;
using Int27 = detail::IntegralType<int32_t, 27>;
using Int28 = detail::IntegralType<int32_t, 28>;
using Int29 = detail::IntegralType<int32_t, 29>;
using Int30 = detail::IntegralType<int32_t, 30>;
using Int31 = detail::IntegralType<int32_t, 31>;
using Int33 = detail::IntegralType<int64_t, 33>;
using Int34 = detail::IntegralType<int64_t, 34>;
using Int35 = detail::IntegralType<int64_t, 35>;
using Int36 = detail::IntegralType<int64_t, 36>;
using Int37 = detail::IntegralType<int64_t, 37>;
using Int38 = detail::IntegralType<int64_t, 38>;
using Int39 = detail::IntegralType<int64_t, 39>;
using Int40 = detail::IntegralType<int64_t, 40>;
using Int41 = detail::IntegralType<int64_t, 41>;
using Int42 = detail::IntegralType<int64_t, 42>;
using Int43 = detail::IntegralType<int64_t, 43>;
using Int44 = detail::IntegralType<int64_t, 44>;
using Int45 = detail::IntegralType<int64_t, 45>;
using Int46 = detail::IntegralType<int64_t, 46>;
using Int47 = detail::IntegralType<int64_t, 47>;
using Int48 = detail::IntegralType<int64_t, 48>;
using Int49 = detail::IntegralType<int64_t, 49>;
using Int50 = detail::IntegralType<int64_t, 50>;
using Int51 = detail::IntegralType<int64_t, 51>;
using Int52 = detail::IntegralType<int64_t, 52>;
using Int53 = detail::IntegralType<int64_t, 53>;
using Int54 = detail::IntegralType<int64_t, 54>;
using Int55 = detail::IntegralType<int64_t, 55>;
using Int56 = detail::IntegralType<int64_t, 56>;
using Int57 = detail::IntegralType<int64_t, 57>;
using Int58 = detail::IntegralType<int64_t, 58>;
using Int59 = detail::IntegralType<int64_t, 59>;
using Int60 = detail::IntegralType<int64_t, 60>;
using Int61 = detail::IntegralType<int64_t, 61>;
using Int62 = detail::IntegralType<int64_t, 62>;
using Int63 = detail::IntegralType<int64_t, 63>;

using UInt1 = detail::IntegralType<uint8_t, 1>;
using UInt2 = detail::IntegralType<uint8_t, 2>;
using UInt3 = detail::IntegralType<uint8_t, 3>;
using UInt4 = detail::IntegralType<uint8_t, 4>;
using UInt5 = detail::IntegralType<uint8_t, 5>;
using UInt6 = detail::IntegralType<uint8_t, 6>;
using UInt7 = detail::IntegralType<uint8_t, 7>;
using UInt9 = detail::IntegralType<uint16_t, 9>;
using UInt10 = detail::IntegralType<uint16_t, 10>;
using UInt11 = detail::IntegralType<uint16_t, 11>;
using UInt12 = detail::IntegralType<uint16_t, 12>;
using UInt13 = detail::IntegralType<uint16_t, 13>;
using UInt14 = detail::IntegralType<uint16_t, 14>;
using UInt15 = detail::IntegralType<uint16_t, 15>;
using UInt17 = detail::IntegralType<uint32_t, 17>;
using UInt18 = detail::IntegralType<uint32_t, 18>;
using UInt19 = detail::IntegralType<uint32_t, 19>;
using UInt20 = detail::IntegralType<uint32_t, 20>;
using UInt21 = detail::IntegralType<uint32_t, 21>;
using UInt22 = detail::IntegralType<uint32_t, 22>;
using UInt23 = detail::IntegralType<uint32_t, 23>;
using UInt24 = detail::IntegralType<uint32_t, 24>;
using UInt25 = detail::IntegralType<uint32_t, 25>;
using UInt26 = detail::IntegralType<uint32_t, 26>;
using UInt27 = detail::IntegralType<uint32_t, 27>;
using UInt28 = detail::IntegralType<uint32_t, 28>;
using UInt29 = detail::IntegralType<uint32_t, 29>;
using UInt30 = detail::IntegralType<uint32_t, 30>;
using UInt31 = detail::IntegralType<uint32_t, 31>;
using UInt33 = detail::IntegralType<uint64_t, 33>;
using UInt34 = detail::IntegralType<uint64_t, 34>;
using UInt35 = detail::IntegralType<uint64_t, 35>;
using UInt36 = detail::IntegralType<uint64_t, 36>;
using UInt37 = detail::IntegralType<uint64_t, 37>;
using UInt38 = detail::IntegralType<uint64_t, 38>;
using UInt39 = detail::IntegralType<uint64_t, 39>;
using UInt40 = detail::IntegralType<uint64_t, 40>;
using UInt41 = detail::IntegralType<uint64_t, 41>;
using UInt42 = detail::IntegralType<uint64_t, 42>;
using UInt43 = detail::IntegralType<uint64_t, 43>;
using UInt44 = detail::IntegralType<uint64_t, 44>;
using UInt45 = detail::IntegralType<uint64_t, 45>;
using UInt46 = detail::IntegralType<uint64_t, 46>;
using UInt47 = detail::IntegralType<uint64_t, 47>;
using UInt48 = detail::IntegralType<uint64_t, 48>;
using UInt49 = detail::IntegralType<uint64_t, 49>;
using UInt50 = detail::IntegralType<uint64_t, 50>;
using UInt51 = detail::IntegralType<uint64_t, 51>;
using UInt52 = detail::IntegralType<uint64_t, 52>;
using UInt53 = detail::IntegralType<uint64_t, 53>;
using UInt54 = detail::IntegralType<uint64_t, 54>;
using UInt55 = detail::IntegralType<uint64_t, 55>;
using UInt56 = detail::IntegralType<uint64_t, 56>;
using UInt57 = detail::IntegralType<uint64_t, 57>;
using UInt58 = detail::IntegralType<uint64_t, 58>;
using UInt59 = detail::IntegralType<uint64_t, 59>;
using UInt60 = detail::IntegralType<uint64_t, 60>;
using UInt61 = detail::IntegralType<uint64_t, 61>;
using UInt62 = detail::IntegralType<uint64_t, 62>;
using UInt63 = detail::IntegralType<uint64_t, 63>;

} // namespace zserio

#endif // ifndef ZSERIO_TYPES_H_INC
8 changes: 8 additions & 0 deletions runtime/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ project(ZserioCpp17RuntimeTest)
include(gtest_utils)
gtest_add_library("${ZSERIO_CPP17_PROJECT_ROOT}/3rdparty/cpp/googletest")

compiler_set_warnings()
compiler_set_warnings_as_errors()
if (SANITIZERS_ENABLED)
compiler_set_undefined_sanitizer()
endif ()

set(ZSERIO_CPP17_RUNTIME_TEST_SRCS
zserio/BitBufferTest.cpp
zserio/BitFieldUtilTest.cpp
Expand All @@ -26,10 +32,12 @@ set(ZSERIO_CPP17_RUNTIME_TEST_SRCS
zserio/FileUtilTest.cpp
zserio/FloatUtilTest.cpp
zserio/HashCodeUtilTest.cpp
zserio/OutOfRangeExceptionTest.cpp
zserio/SizeConvertUtilTest.cpp
zserio/SpanTest.cpp
zserio/StringConvertUtilTest.cpp
zserio/TrackingAllocator.h
zserio/TypesTest.cpp
)

add_executable(${PROJECT_NAME} ${ZSERIO_CPP17_RUNTIME_TEST_SRCS})
Expand Down
17 changes: 17 additions & 0 deletions runtime/test/zserio/OutOfRangeExceptionTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "gtest/gtest.h"
#include "zserio/OutOfRangeException.h"

namespace zserio
{

TEST(OutOfRangeExceptionTest, correctTypeAfterAppend)
{
ASSERT_THROW(
{
throw OutOfRangeException()
<< "Test that appending using operator<< persists the exception type!";
},
OutOfRangeException);
}

} // namespace zserio
Loading

0 comments on commit 288dffb

Please sign in to comment.