mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-28 06:55:50 +00:00
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
This commit is contained in:
committed by
Vinnie Falco
parent
c0dfbdc910
commit
a45b532664
@@ -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"
|
||||
|
||||
247
beast/utility/tagged_integer.h
Normal file
247
beast/utility/tagged_integer.h
Normal file
@@ -0,0 +1,247 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2014, Nikolaos D. Bougalis <nikb@bougalis.net>
|
||||
|
||||
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 <functional>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
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 Int, class Tag>
|
||||
class tagged_integer
|
||||
{
|
||||
private:
|
||||
friend struct is_contiguously_hashable<tagged_integer <Int, Tag>>;
|
||||
|
||||
static_assert (std::is_unsigned <Int>::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 <OtherInt>::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 <class OtherInt>
|
||||
typename std::enable_if <
|
||||
std::is_integral <OtherInt>::value &&
|
||||
sizeof (OtherInt) <= sizeof (Int),
|
||||
tagged_integer <Int, Tag>>::type&
|
||||
operator+= (OtherInt rhs) noexcept
|
||||
{
|
||||
m_value += rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class OtherInt>
|
||||
typename std::enable_if <
|
||||
std::is_integral <OtherInt>::value &&
|
||||
sizeof (OtherInt) <= sizeof (Int),
|
||||
tagged_integer <Int, Tag>>::type&
|
||||
operator-= (OtherInt rhs) noexcept
|
||||
{
|
||||
m_value -= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class OtherInt>
|
||||
friend
|
||||
typename std::enable_if <
|
||||
std::is_integral <OtherInt>::value &&
|
||||
sizeof (OtherInt) <= sizeof (Int),
|
||||
tagged_integer <Int, Tag>>::type
|
||||
operator+ (tagged_integer const& lhs,
|
||||
OtherInt rhs) noexcept
|
||||
{
|
||||
return tagged_integer (lhs.m_value + rhs);
|
||||
}
|
||||
|
||||
template <class OtherInt>
|
||||
friend
|
||||
typename std::enable_if <
|
||||
std::is_integral <OtherInt>::value &&
|
||||
sizeof (OtherInt) <= sizeof (Int),
|
||||
tagged_integer <Int, Tag>>::type
|
||||
operator+ (OtherInt lhs,
|
||||
tagged_integer const& rhs) noexcept
|
||||
{
|
||||
return tagged_integer (lhs + rhs.m_value);
|
||||
}
|
||||
|
||||
template <class OtherInt>
|
||||
friend
|
||||
typename std::enable_if <
|
||||
std::is_integral <OtherInt>::value &&
|
||||
sizeof (OtherInt) <= sizeof (Int),
|
||||
tagged_integer <Int, Tag>>::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 <class Int, class Tag>
|
||||
struct is_contiguously_hashable<tagged_integer<Int, Tag>>
|
||||
: public is_contiguously_hashable<Int>
|
||||
{
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
154
beast/utility/tests/tagged_integer.test.cpp
Normal file
154
beast/utility/tests/tagged_integer.test.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2014, Nikolaos D. Bougalis <nikb@bougalis.net>
|
||||
|
||||
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 <type_traits>
|
||||
|
||||
#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 <std::uint32_t, Tag1> TagInt1;
|
||||
typedef tagged_integer <std::uint32_t, Tag2> TagInt2;
|
||||
typedef tagged_integer <std::uint64_t, Tag1> TagInt3;
|
||||
|
||||
// Check construction of tagged_integers
|
||||
static_assert (std::is_constructible<TagInt1, std::uint32_t>::value,
|
||||
"TagInt1 should be constructible using a std::uint32_t");
|
||||
|
||||
static_assert (!std::is_constructible<TagInt1, std::uint64_t>::value,
|
||||
"TagInt1 should not be constructible using a std::uint64_t");
|
||||
|
||||
static_assert (std::is_constructible<TagInt3, std::uint32_t>::value,
|
||||
"TagInt3 should be constructible using a std::uint32_t");
|
||||
|
||||
static_assert (std::is_constructible<TagInt3, std::uint64_t>::value,
|
||||
"TagInt3 should be constructible using a std::uint64_t");
|
||||
|
||||
// Check assignment of tagged_integers
|
||||
static_assert (!std::is_assignable<TagInt1, std::uint32_t>::value,
|
||||
"TagInt1 should not be assignable with a std::uint32_t");
|
||||
|
||||
static_assert (!std::is_assignable<TagInt1, std::uint64_t>::value,
|
||||
"TagInt1 should not be assignable with a std::uint64_t");
|
||||
|
||||
static_assert (!std::is_assignable<TagInt3, std::uint32_t>::value,
|
||||
"TagInt3 should not be assignable with a std::uint32_t");
|
||||
|
||||
static_assert (!std::is_assignable<TagInt3, std::uint64_t>::value,
|
||||
"TagInt3 should not be assignable with a std::uint64_t");
|
||||
|
||||
static_assert (std::is_assignable<TagInt1, TagInt1>::value,
|
||||
"TagInt1 should be assignable with a TagInt1");
|
||||
|
||||
static_assert (!std::is_assignable<TagInt1, TagInt2>::value,
|
||||
"TagInt1 should not be assignable with a TagInt2");
|
||||
|
||||
static_assert (std::is_assignable<TagInt3, TagInt3>::value,
|
||||
"TagInt3 should be assignable with a TagInt1");
|
||||
|
||||
static_assert (!std::is_assignable<TagInt1, TagInt3>::value,
|
||||
"TagInt1 should not be assignable with a TagInt3");
|
||||
|
||||
static_assert (!std::is_assignable<TagInt3, TagInt1>::value,
|
||||
"TagInt3 should not be assignable with a TagInt1");
|
||||
|
||||
// Check convertibility of tagged_integers
|
||||
static_assert (!std::is_convertible<std::uint32_t, TagInt1>::value,
|
||||
"std::uint32_t should not be convertible to a TagInt1");
|
||||
|
||||
static_assert (!std::is_convertible<std::uint32_t, TagInt3>::value,
|
||||
"std::uint32_t should not be convertible to a TagInt3");
|
||||
|
||||
static_assert (!std::is_convertible<std::uint64_t, TagInt3>::value,
|
||||
"std::uint64_t should not be convertible to a TagInt3");
|
||||
|
||||
static_assert (!std::is_convertible<std::uint64_t, TagInt2>::value,
|
||||
"std::uint64_t should not be convertible to a TagInt2");
|
||||
|
||||
static_assert (!std::is_convertible<TagInt1, TagInt2>::value,
|
||||
"TagInt1 should not be convertible to TagInt2");
|
||||
|
||||
static_assert (!std::is_convertible<TagInt1, TagInt3>::value,
|
||||
"TagInt1 should not be convertible to TagInt3");
|
||||
|
||||
static_assert (!std::is_convertible<TagInt2, TagInt3>::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
|
||||
Reference in New Issue
Block a user