From a45b532664015df7f02eed86b62e65eb1003aa91 Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Fri, 21 Mar 2014 09:31:15 -0700 Subject: [PATCH] Add beast::tagged_integer: * Wraps standard integer types to provide type-safety * Named types provide self-documenting semantics * Catches programmer errors involving mismatched types at compile time * Operators restrict mutation to only safe and meaningful operations --- beast/utility/Utility.cpp | 1 + beast/utility/tagged_integer.h | 247 ++++++++++++++++++++ beast/utility/tests/tagged_integer.test.cpp | 154 ++++++++++++ 3 files changed, 402 insertions(+) create mode 100644 beast/utility/tagged_integer.h create mode 100644 beast/utility/tests/tagged_integer.test.cpp diff --git a/beast/utility/Utility.cpp b/beast/utility/Utility.cpp index 380e2224d5..a05169f91f 100644 --- a/beast/utility/Utility.cpp +++ b/beast/utility/Utility.cpp @@ -32,3 +32,4 @@ #include "tests/bassert.test.cpp" #include "tests/empty_base_optimization.test.cpp" #include "tests/Zero.test.cpp" +#include "tests/tagged_integer.test.cpp" diff --git a/beast/utility/tagged_integer.h b/beast/utility/tagged_integer.h new file mode 100644 index 0000000000..e4c3d8c878 --- /dev/null +++ b/beast/utility/tagged_integer.h @@ -0,0 +1,247 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2014, Nikolaos D. Bougalis + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_UTILITY_TAGGED_INTEGER_H_INCLUDED +#define BEAST_UTILITY_TAGGED_INTEGER_H_INCLUDED + +#include "../container/hash_append.h" + +#include "noexcept.h" + +#include +#include +#include +#include + +namespace beast { + +/** A type-safe wrap around standard unsigned integral types + + The tag is used to implement type safety, catching mismatched types at + compile time. Multiple instantiations wrapping the same underlying integral + type are distinct types (distinguished by tag) and will not interoperate. + + A tagged_integer supports all the comparison operators that are available + for the underlying integral type. It only supports a subset of arithmetic + operators, restricting mutation to only safe and meaningful types. +*/ +template +class tagged_integer +{ +private: + friend struct is_contiguously_hashable>; + + static_assert (std::is_unsigned ::value, + "The specified Int type must be unsigned"); + + Int m_value; + +public: + typedef Int value_type; + typedef Tag tag_type; + + tagged_integer() = default; + + template < + class OtherInt, + class = typename std::enable_if < + std::is_integral ::value && + sizeof (OtherInt) <= sizeof (Int) + >::type + > + explicit + /* constexpr */ + tagged_integer (OtherInt value) noexcept + : m_value (value) + { + } + + // Arithmetic operators + tagged_integer& + operator++ () noexcept + { + ++m_value; + return *this; + } + + tagged_integer + operator++ (int) noexcept + { + tagged_integer orig (*this); + ++(*this); + return orig; + } + + tagged_integer& + operator-- () noexcept + { + --m_value; + return *this; + } + + tagged_integer + operator-- (int) noexcept + { + tagged_integer orig (*this); + --(*this); + return orig; + } + + template + typename std::enable_if < + std::is_integral ::value && + sizeof (OtherInt) <= sizeof (Int), + tagged_integer >::type& + operator+= (OtherInt rhs) noexcept + { + m_value += rhs; + return *this; + } + + template + typename std::enable_if < + std::is_integral ::value && + sizeof (OtherInt) <= sizeof (Int), + tagged_integer >::type& + operator-= (OtherInt rhs) noexcept + { + m_value -= rhs; + return *this; + } + + template + friend + typename std::enable_if < + std::is_integral ::value && + sizeof (OtherInt) <= sizeof (Int), + tagged_integer >::type + operator+ (tagged_integer const& lhs, + OtherInt rhs) noexcept + { + return tagged_integer (lhs.m_value + rhs); + } + + template + friend + typename std::enable_if < + std::is_integral ::value && + sizeof (OtherInt) <= sizeof (Int), + tagged_integer >::type + operator+ (OtherInt lhs, + tagged_integer const& rhs) noexcept + { + return tagged_integer (lhs + rhs.m_value); + } + + template + friend + typename std::enable_if < + std::is_integral ::value && + sizeof (OtherInt) <= sizeof (Int), + tagged_integer >::type + operator- (tagged_integer const& lhs, + OtherInt rhs) noexcept + { + return tagged_integer (lhs.m_value - rhs); + } + + friend + Int + operator- (tagged_integer const& lhs, + tagged_integer const& rhs) noexcept + { + return lhs.m_value - rhs.m_value; + } + + // Comparison operators + friend + bool + operator== (tagged_integer const& lhs, + tagged_integer const& rhs) noexcept + { + return lhs.m_value == rhs.m_value; + } + + friend + bool + operator!= (tagged_integer const& lhs, + tagged_integer const& rhs) noexcept + { + return lhs.m_value != rhs.m_value; + } + + friend + bool + operator< (tagged_integer const& lhs, + tagged_integer const& rhs) noexcept + { + return lhs.m_value < rhs.m_value; + } + + friend + bool + operator<= (tagged_integer const& lhs, + tagged_integer const& rhs) noexcept + { + return lhs.m_value <= rhs.m_value; + } + + friend + bool + operator> (tagged_integer const& lhs, + tagged_integer const& rhs) noexcept + { + return lhs.m_value > rhs.m_value; + } + + friend + bool + operator>= (tagged_integer const& lhs, + tagged_integer const& rhs) noexcept + { + return lhs.m_value >= rhs.m_value; + } + + friend + std::ostream& + operator<< (std::ostream& s, tagged_integer const& t) + { + s << t.m_value; + return s; + } + + friend + std::istream& + operator>> (std::istream& s, tagged_integer& t) + { + s >> t.m_value; + return s; + } +}; + +template +struct is_contiguously_hashable> + : public is_contiguously_hashable +{ +}; + +} + +#endif + diff --git a/beast/utility/tests/tagged_integer.test.cpp b/beast/utility/tests/tagged_integer.test.cpp new file mode 100644 index 0000000000..4e0a383f62 --- /dev/null +++ b/beast/utility/tests/tagged_integer.test.cpp @@ -0,0 +1,154 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2014, Nikolaos D. Bougalis + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#if BEAST_INCLUDE_BEASTCONFIG +#include "../../../BeastConfig.h" +#endif + +#include + +#include "../tagged_integer.h" +#include "../../unit_test/suite.h" + +namespace beast { + +class tagged_integer_test + : public unit_test::suite +{ +private: + struct Tag1 { }; + struct Tag2 { }; + + typedef tagged_integer TagInt1; + typedef tagged_integer TagInt2; + typedef tagged_integer TagInt3; + + // Check construction of tagged_integers + static_assert (std::is_constructible::value, + "TagInt1 should be constructible using a std::uint32_t"); + + static_assert (!std::is_constructible::value, + "TagInt1 should not be constructible using a std::uint64_t"); + + static_assert (std::is_constructible::value, + "TagInt3 should be constructible using a std::uint32_t"); + + static_assert (std::is_constructible::value, + "TagInt3 should be constructible using a std::uint64_t"); + + // Check assignment of tagged_integers + static_assert (!std::is_assignable::value, + "TagInt1 should not be assignable with a std::uint32_t"); + + static_assert (!std::is_assignable::value, + "TagInt1 should not be assignable with a std::uint64_t"); + + static_assert (!std::is_assignable::value, + "TagInt3 should not be assignable with a std::uint32_t"); + + static_assert (!std::is_assignable::value, + "TagInt3 should not be assignable with a std::uint64_t"); + + static_assert (std::is_assignable::value, + "TagInt1 should be assignable with a TagInt1"); + + static_assert (!std::is_assignable::value, + "TagInt1 should not be assignable with a TagInt2"); + + static_assert (std::is_assignable::value, + "TagInt3 should be assignable with a TagInt1"); + + static_assert (!std::is_assignable::value, + "TagInt1 should not be assignable with a TagInt3"); + + static_assert (!std::is_assignable::value, + "TagInt3 should not be assignable with a TagInt1"); + + // Check convertibility of tagged_integers + static_assert (!std::is_convertible::value, + "std::uint32_t should not be convertible to a TagInt1"); + + static_assert (!std::is_convertible::value, + "std::uint32_t should not be convertible to a TagInt3"); + + static_assert (!std::is_convertible::value, + "std::uint64_t should not be convertible to a TagInt3"); + + static_assert (!std::is_convertible::value, + "std::uint64_t should not be convertible to a TagInt2"); + + static_assert (!std::is_convertible::value, + "TagInt1 should not be convertible to TagInt2"); + + static_assert (!std::is_convertible::value, + "TagInt1 should not be convertible to TagInt3"); + + static_assert (!std::is_convertible::value, + "TagInt2 should not be convertible to a TagInt3"); + +public: + void run () + { + TagInt1 const zero (0); + TagInt1 const one (1); + + testcase ("Comparison Operators"); + + expect (zero >= zero, "Should be greater than or equal"); + expect (zero == zero, "Should be equal"); + + expect (one > zero, "Should be greater"); + expect (one >= zero, "Should be greater than or equal"); + expect (one != zero, "Should not be equal"); + + unexpected (one < zero, "Should be greater"); + unexpected (one <= zero, "Should not be greater than or equal"); + unexpected (one == zero, "Should not be equal"); + + testcase ("Arithmetic Operators"); + + TagInt1 tmp; + + tmp = zero + 0u; + expect (tmp == zero, "Should be equal"); + + tmp = 1u + zero; + expect (tmp == one, "Should be equal"); + + expect(--tmp == zero, "Should be equal"); + expect(tmp++ == zero, "Should be equal"); + expect(tmp == one, "Should be equal"); + + expect(tmp-- == one, "Should be equal"); + expect(tmp == zero, "Should be equal"); + expect(++tmp == one, "Should be equal"); + + tmp = zero; + + tmp += 1u; + expect(tmp == one, "Should be equal"); + + tmp -= 1u; + expect(tmp == zero, "Should be equal"); + } +}; + +BEAST_DEFINE_TESTSUITE(tagged_integer,utility,beast); + +} // beast