Skip to content

Commit

Permalink
Merge pull request #1770 from cyrossignol/polls
Browse files Browse the repository at this point in the history
Generalize enum serialization
  • Loading branch information
jamescowens committed Jul 1, 2020
2 parents dc6ac5c + 35fbcd3 commit b52a5fa
Show file tree
Hide file tree
Showing 8 changed files with 325 additions and 148 deletions.
1 change: 1 addition & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ GRIDCOIN_CORE_H = \
compat/endian.h \
contract/polls.h \
crypter.h \
enumbytes.h \
filehash.h \
fs.h \
fwd.h \
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ GRIDCOIN_TESTS =\
test/base64_tests.cpp \
test/bignum_tests.cpp \
test/block_tests.cpp \
test/enumbytes.cpp \
test/fs_tests.cpp \
test/getarg_tests.cpp \
test/gridcoin_tests.cpp \
Expand Down
155 changes: 155 additions & 0 deletions src/enumbytes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#pragma once

#include "serialize.h"

//!
//! \brief A wrapper around an enum type that provides for serialization of the
//! enum values as unsigned integers.
//!
//! This type provides boilerplate useful for preserving the semantics of scoped
//! enumerations (enum class) while facilitating serialization of the values.
//!
//! \tparam E Enum type wrapped by this class.
//! \tparam U Underlying unsigned integer type to serialize the enum value as.
//! \tparam B Enum value that describes the upper bound of the enum range.
//!
template <typename E, typename U, E B>
class EnumBytes
{
public:
//!
//! \brief The greatest valid value in the wrapped enum.
//!
static constexpr size_t MAX = static_cast<size_t>(B) - 1;

static_assert(std::is_enum<E>::value, "EnumBytes<E,U,B>: E is not an enum");
static_assert(std::is_unsigned<U>::value, "EnumBytes<E,U,B>: U is signed");
static_assert(MAX <= std::numeric_limits<U>::max(),
"EnumBytes<E,U,B>: upper bound B for enum E exceeds range of type U");

//!
//! \brief Serializes and deserializes enum values from a stream.
//!
//! This type can be used to serialize enums without storing the value as
//! an EnumBytes<E, U, B> object.
//!
class Formatter
{
public:
//!
//! \brief Serialize an enum value as a byte to the provided stream.
//!
//! \param stream The output stream.
//!
template <typename Stream>
void Ser(Stream& s, E e)
{
::Serialize(s, EnumBytes<E, U, B>(e).Raw());
}

//!
//! \brief Deserialize an enum value from the provided stream.
//!
//! \param stream The input stream.
//!
template <typename Stream>
void Unser(Stream& s, E& e)
{
U raw;
::Unserialize(s, raw);

if (raw > EnumBytes<E, U, B>::MAX) {
throw std::ios_base::failure("EnumBytes out of range");
}

e = static_cast<E>(raw);
}
};

//!
//! \brief Wrap the provided enum value.
//!
//! \param value The enum value to wrap.
//!
constexpr EnumBytes(E value) noexcept : m_value(value)
{
}

constexpr bool operator==(const E& other) const noexcept { return m_value == other; }
constexpr bool operator==(const EnumBytes<E, U, B>& other) const noexcept { return *this == other.m_value; }
constexpr bool operator!=(const E& other) const noexcept { return !(*this == other); }
constexpr bool operator!=(const EnumBytes<E, U, B>& other) const noexcept { return !(*this == other); }
constexpr bool operator<(const E& other) const noexcept { return m_value < other; }
constexpr bool operator<(const EnumBytes<E, U, B>& other) const noexcept { return *this < other.m_value; }
constexpr bool operator<=(const E& other) const noexcept { return m_value <= other; }
constexpr bool operator<=(const EnumBytes<E, U, B>& other) const noexcept { return *this <= other.m_value; }
constexpr bool operator>(const E& other) const noexcept { return m_value > other; }
constexpr bool operator>(const EnumBytes<E, U, B>& other) const noexcept { return *this > other.m_value; }
constexpr bool operator>=(const E& other) const noexcept { return m_value >= other; }
constexpr bool operator>=(const EnumBytes<E, U, B>& other) const noexcept { return *this >= other.m_value; }

//!
//! \brief Get the wrapped enum value.
//!
//! \return A value enumerated on enum \c E.
//!
constexpr E Value() const noexcept
{
return m_value;
}

//!
//! \brief Get the wrapped enum value as a value of the underlying type.
//!
//! \return For example, an unsigned char for an enum that represents
//! an underlying byte value.
//!
constexpr uint8_t Raw() const noexcept
{
return static_cast<uint8_t>(m_value);
}

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(Using<Formatter>(m_value));
}

protected:
E m_value; //!< The wrapped enum value.
}; // EnumBytes<E, U, B>

//!
//! \brief A wrapper around an enum type that provides for serialization of the
//! enum values as single bytes.
//!
//! This type expects a wrapped enum to contain an OUT_OF_BOUND identifier for
//! the value greater than the upper bound of the enumerated range. A value of
//! zero should map to a meaningful identifier in the enum.
//!
//! \tparam E Enum type wrapped by this class.
//!
template <typename E>
class EnumByte : public EnumBytes<E, uint8_t, E::OUT_OF_BOUND>
{
public:
//!
//! \brief Initialize an enum wrapper to zero.
//!
constexpr EnumByte() noexcept
: EnumByte(static_cast<E>(0))
{
}

//!
//! \brief Wrap the provided enum value.
//!
//! \param value The enum value to wrap.
//!
constexpr EnumByte(E value) noexcept
: EnumBytes<E, uint8_t, E::OUT_OF_BOUND>(value)
{
}
};
8 changes: 6 additions & 2 deletions src/neuralnet/contract/contract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ void Contract::Log(const std::string& prefix) const
// Class: Contract::Type
// -----------------------------------------------------------------------------

Contract::Type::Type(ContractType type) : WrappedEnum(type)
Contract::Type::Type(ContractType type) : EnumByte(type)
{
}

Expand Down Expand Up @@ -760,7 +760,7 @@ std::string Contract::Type::ToString() const
// Class: Contract::Action
// -----------------------------------------------------------------------------

Contract::Action::Action(ContractAction action) : WrappedEnum(action)
Contract::Action::Action(ContractAction action) : EnumByte(action)
{
}

Expand Down Expand Up @@ -832,6 +832,8 @@ ContractPayload Contract::Body::ConvertFromLegacy(const ContractType type) const
return m_payload;
case ContractType::VOTE:
return m_payload;
case ContractType::OUT_OF_BOUND:
assert(false);
}

return ContractPayload::Make<EmptyPayload>();
Expand Down Expand Up @@ -864,6 +866,8 @@ void Contract::Body::ResetType(const ContractType type)
case ContractType::VOTE:
m_payload.Reset(new LegacyPayload());
break;
case ContractType::OUT_OF_BOUND:
assert(false);
}
}

Expand Down
118 changes: 5 additions & 113 deletions src/neuralnet/contract/contract.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "enumbytes.h"
#include "key.h"
#include "neuralnet/contract/payload.h"
#include "serialize.h"
Expand Down Expand Up @@ -27,115 +28,6 @@ namespace NN {
//!
class Contract
{
private:
//!
//! \brief A wrapper around an enum type.
//!
//! \tparam E The enum type wrapped by this class.
//!
template<typename E>
struct WrappedEnum
{
// Replace with E::underlying_type_t when moving to C++14:
using EnumUnderlyingType = typename std::underlying_type<E>::type;

//!
//! \brief Compare a supplied enum value for equality.
//!
//! \param other An enum value to check equality for.
//!
//! \return \c true if the suppled value matches the wrapped enum value.
//!
bool operator==(const E& other) const
{
return m_value == other;
}

//!
//! \brief Compare a supplied enum value for inequality.
//!
//! \param other An enum value to check inequality for.
//!
//! \return \c true if the suppled value does not match the wrapped
//! enum value.
//!
bool operator!=(const E& other) const
{
return m_value != other;
}

//!
//! \brief Get the wrapped enum value.
//!
//! \return A value enumerated on enum \c E.
//!
E Value() const
{
return m_value;
}

//!
//! \brief Get the wrapped enum value as a value of the underlying type.
//!
//! \return For example, an unsigned char for an enum that represents
//! an underlying byte value.
//!
EnumUnderlyingType Raw() const
{
return static_cast<EnumUnderlyingType>(m_value);
}

//!
//! \brief Get the string representation of the wrapped enum value.
//!
//! \return The string as it would appear in a transaction message or
//! the captured string if parsed from an unrecognized value.
//!
virtual std::string ToString() const = 0;

//!
//! \brief Serialize the wrapped enum value to the provided stream.
//!
//! \param stream The output stream.
//!
template<typename Stream>
void Serialize(Stream& stream) const
{
::Serialize(stream, Raw());
}

//!
//! \brief Deserialize an enum value from the provided stream.
//!
//! \param stream The input stream.
//!
template<typename Stream>
void Unserialize(Stream& stream)
{
EnumUnderlyingType value;

::Unserialize(stream, value);

if (value > static_cast<EnumUnderlyingType>(E::MAX_VALUE)) {
m_value = E::UNKNOWN;
} else {
m_value = static_cast<E>(value);
}
}

protected:
E m_value; //!< The wrapped enum value.

//!
//! \brief Delegated constructor called by child types.
//!
//! \param value The enum value to wrap.
//!
WrappedEnum(E value) : m_value(value)
{
}
}; // Contract::WrappedEnum

public:
//!
//! \brief Version number of the current format for a serialized contract.
Expand All @@ -155,7 +47,7 @@ class Contract
//!
//! \brief A contract type from a transaction message.
//!
struct Type : public WrappedEnum<ContractType>
struct Type : public EnumByte<ContractType>
{
//!
//! \brief Initialize an instance for a \c ContractType value.
Expand All @@ -180,13 +72,13 @@ class Contract
//!
//! \return The string as it would appear in a legacy transaction message.
//!
std::string ToString() const override;
std::string ToString() const;
}; // Contract::Type

//!
//! \brief A contract action from a transaction message.
//!
struct Action : public WrappedEnum<ContractAction>
struct Action : public EnumByte<ContractAction>
{
//!
//! \brief Initialize an instance for a \c ContractAction value.
Expand All @@ -211,7 +103,7 @@ class Contract
//!
//! \return The string as it would appear in a transaction message.
//!
std::string ToString() const override;
std::string ToString() const;
}; // Contract::Action

//!
Expand Down
Loading

0 comments on commit b52a5fa

Please sign in to comment.