7
\$\begingroup\$

There are a lot of times where you need to ensure that a number stays within some constraints and I got annoyed of inventing a distinct class for every case so I decided to solve the problem once and for all. So I wrote a template which has semantics the same as a number except it checks the predicate each time its value is about to change.

Here are some design decisions:

  • Compile time binding of predicate. I don't need runtime polymorphism.
  • The violation of predicate should throw exception.
  • The template will decay to underlying type as early as possible to allow intermediate values violate the predicate.
  • It should be usable in any construct that accepts fundamental arithmetic types.
  • On the other hand, it should work for arbitrary precision types as long as they have the same interface as fundamental types.
  • It should be possible to derive from the template to add additional functions.

Declarations:

/// \file
/// \brief Header file that describes the CheckedNumber class template.
/// \author Lyberta
/// \copyright GNU GPLv3 or any later version.

#pragma once

#include <istream>
#include <ostream>

namespace ftz
{
namespace General
{

/// \brief A concept for a predicate.
template <typename T>
concept bool Predicate()
{
    return requires()
    {
        typename T::ValueType;
    } &&
    requires (typename T::ValueType value)
    {
        {T::Check(value)} -> void;
    };
}

/// \brief A checked number.
/// \details Checked number is a number which can't have some values which are
/// otherwise possible for the underlying type. For example, a floating point
/// number which can't be negative. This class takes a predicate class which
/// defines the underlying type of the value and a static function to check the
/// value. Predicate must throw std::domain_error if the value is illegal.
/// \tparam T Type of the predicate.

template <Predicate T>
class CheckedNumber
{
public:
    using ValueType = typename T::ValueType; ///< Underlying type of the value.

    /// \brief Constructor.
    /// \param[in] val Value to set.
    /// \throw std::domain_error If value is illegal.
    constexpr explicit CheckedNumber(ValueType val = ValueType{});

    /// \brief Returns the reference to the underlying value.
    /// \return Reference to the underlying value.
    constexpr const ValueType& GetValue() const noexcept;

    /// \brief Sets the underlying value.
    /// \param[in] newvalue Value to set.
    /// \return No return.
    constexpr void SetValue(ValueType newvalue);

    /// \brief Returns the underlying value.
    /// \return Underlying value.
    constexpr operator ValueType() const noexcept;

    /// \brief Sets the underlying value.
    /// \param[in] newvalue Value to set.
    /// \return Reference to this number.
    /// \throw std::domain_error If value is illegal.
    constexpr CheckedNumber& operator=(ValueType newvalue);

    /// \brief Converts the underlying value to boolean.
    /// \return Result of the conversion.
    constexpr explicit operator bool() const;

    /// \brief Converts the underlying value to boolean and negates it.
    /// \return Negated result of the conversion.
    constexpr bool operator!() const;

    /// \brief Returns the underlying value.
    /// \return Underlying value.
    constexpr auto operator+() const;

    /// \brief Returns the underlying value with changed sign.
    /// \return Underlying value with changed sign.
    constexpr auto operator-() const;

    /// \brief Returns the binary negated underlying value.
    /// \return Binary negated underlying value.
    constexpr auto operator~() const;

    /// \brief Preincrements the underlying value.
    /// \return Reference to this number.
    /// \throw std::domain_error if result is illegal.
    CheckedNumber& operator++();

    /// \brief Postincrements the underlying value.
    /// \return Copy of this number before increment.
    /// \throw std::domain_error if result is illegal.
    CheckedNumber operator++(int);

    /// \brief Predecrements the underlying value.
    /// \return Reference to this number.
    /// \throw std::domain_error if result is illegal.
    CheckedNumber& operator--();

    /// \brief Postdecrements the underlying value.
    /// \return Copy of this number before decrement.
    /// \throw std::domain_error if result is illegal.
    CheckedNumber operator--(int);

    /// \brief Adds a value to the underlying value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to add.
    /// \return Reference to this number.
    /// \throw std::domain_error If the sum is illegal.
    template <typename U>
    CheckedNumber& operator+=(const U& val);

    /// \brief Subtracts a value from the underlying value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to subtract.
    /// \return Reference to this number.
    /// \throw std::domain_error If the difference is illegal.
    template <typename U>
    CheckedNumber& operator-=(const U& val);

    /// \brief Mulptiplies the underlying value by another value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to multiply by.
    /// \return Reference to this number.
    /// \throw std::domain_error If the product is illegal.
    template <typename U>
    CheckedNumber& operator*=(const U& val);

    /// \brief Divides the underlying value by another value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to divide by.
    /// \return Reference to this number.
    /// \throw std::domain_error If the quotient is illegal.
    template <typename U>
    CheckedNumber& operator/=(const U& val);

    /// \brief Computes the remainder of the division by another value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to divide by.
    /// \return Reference to this number.
    /// \throw std::domain_error If the remainder is illegal.
    template <typename U>
    CheckedNumber& operator%=(const U& val);

    /// \brief Computes bitwise AND of underlying value and another value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to compute with.
    /// \return Reference to this number.
    /// \throw std::domain_error If the result is illegal.
    template <typename U>
    CheckedNumber& operator&=(const U& val);

    /// \brief Computes bitwise OR of underlying value and another value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to compute with.
    /// \return Reference to this number.
    /// \throw std::domain_error If the result is illegal.
    template <typename U>
    CheckedNumber& operator|=(const U& val);

    /// \brief Computes bitwise XOR of underlying value and another value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to compute with.
    /// \return Reference to this number.
    /// \throw std::domain_error If the result is illegal.
    template <typename U>
    CheckedNumber& operator^=(const U& val);

    /// \brief Shifts the underlying value by several places to the left.
    /// \tparam U Type of the value.
    /// \param[in] val Value to shift by.
    /// \return Reference to this number.
    /// \throw std::domain_error If the result is illegal.
    template <typename U>
    CheckedNumber& operator<<=(const U& val);

    /// \brief Shifts the underlying value by several places to the right.
    /// \tparam U Type of the value.
    /// \param[in] val Value to shift by.
    /// \return Reference to this number.
    /// \throw std::domain_error If the result is illegal.
    template <typename U>
    CheckedNumber& operator>>=(const U& val);
private:
    ValueType value; ///< Underlying value.
};

/// \brief Adds two numbers together.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Sum of the numbers.
template <Predicate T, Predicate U>
auto operator+(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Adds two numbers together.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Sum of the numbers.
template <Predicate T, typename U>
auto operator+(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Adds two numbers together.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Sum of the numbers.
template <Predicate T, typename U>
auto operator+(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Subtracts one number from the other.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs Number to subtract from.
/// \param[in] rhs Number to subtracts.
/// \return Difference between the numbers.
template <Predicate T, Predicate U>
auto operator-(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Subtracts one number from the other.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs Number to subtract from.
/// \param[in] rhs Number to subtracts.
/// \return Difference between the numbers.
template <Predicate T, typename U>
auto operator-(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Subtracts one number from the other.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs Number to subtract from.
/// \param[in] rhs Number to subtracts.
/// \return Difference between the numbers.
template <Predicate T, typename U>
auto operator-(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Multiplies two numbers together.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Product of the numbers.
template <Predicate T, Predicate U>
auto operator*(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Multiplies two numbers together.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Product of the numbers.
template <Predicate T, typename U>
auto operator*(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Multiplies two numbers together.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Product of the numbers.
template <Predicate T, typename U>
auto operator*(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Divides one number by the other.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs Number to divide.
/// \param[in] rhs Number to divide by.
/// \return Quotient of the numbers.
template <Predicate T, Predicate U>
auto operator/(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Divides one number by the other.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs Number to divide.
/// \param[in] rhs Number to divide by.
/// \return Quotient of the numbers.
template <Predicate T, typename U>
auto operator/(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Divides one number by the other.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs Number to divide.
/// \param[in] rhs Number to divide by.
/// \return Quotient of the numbers.
template <Predicate T, typename U>
auto operator/(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Returns the remainder of the division of two numbers.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs Number to divide.
/// \param[in] rhs Number to divide by.
/// \return Remainder of the division.
template <Predicate T, Predicate U>
auto operator%(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Returns the remainder of the division of two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs Number to divide.
/// \param[in] rhs Number to divide by.
/// \return Remainder of the division.
template <Predicate T, typename U>
auto operator%(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Returns the remainder of the division of two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs Number to divide.
/// \param[in] rhs Number to divide by.
/// \return Remainder of the division.
template <Predicate T, typename U>
auto operator%(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Returns the bitwise AND of two numbers.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Bitwise AND of two numbers.
template <Predicate T, Predicate U>
auto operator&(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Returns the bitwise AND of two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Bitwise AND of two numbers.
template <Predicate T, typename U>
auto operator&(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Returns the bitwise AND of two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Bitwise AND of two numbers.
template <Predicate T, typename U>
auto operator&(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Returns the bitwise OR of two numbers.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Bitwise OR of two numbers.
template <Predicate T, Predicate U>
auto operator|(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Returns the bitwise OR of two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Bitwise OR of two numbers.
template <Predicate T, typename U>
auto operator|(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Returns the bitwise OR of two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Bitwise OR of two numbers.
template <Predicate T, typename U>
auto operator|(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Returns the bitwise XOR of two numbers.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Bitwise OR of two numbers.
template <Predicate T, Predicate U>
auto operator^(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Returns the bitwise XOR of two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Bitwise OR of two numbers.
template <Predicate T, typename U>
auto operator^(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Returns the bitwise XOR of two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Bitwise OR of two numbers.
template <Predicate T, typename U>
auto operator^(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Returns the shift of the number by several places to the left.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs Number to shift.
/// \param[in] rhs Number to shift by.
/// \return Result of the shift.
template <Predicate T, Predicate U>
auto operator<<(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Returns the shift of the number by several places to the left.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs Number to shift.
/// \param[in] rhs Number to shift by.
/// \return Result of the shift.
template <Predicate T, typename U>
auto operator<<(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Returns the shift of the number by several places to the left.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs Number to shift.
/// \param[in] rhs Number to shift by.
/// \return Result of the shift.
template <Predicate T, typename U,
std::enable_if_t<!std::is_base_of<std::ios_base, U>::value>>
//std::enable_if_t<!std::is_base_of_v<std::ios_base, U>>> C++17
auto operator<<(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Returns the shift of the number by several places to the right.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs Number to shift.
/// \param[in] rhs Number to shift by.
/// \return Result of the shift.
template <Predicate T, Predicate U>
auto operator>>(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Returns the shift of the number by several places to the right.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs Number to shift.
/// \param[in] rhs Number to shift by.
/// \return Result of the shift.
template <Predicate T, typename U>
auto operator>>(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Returns the shift of the number by several places to the right.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs Number to shift.
/// \param[in] rhs Number to shift by.
/// \return Result of the shift.
template <Predicate T, typename U,
std::enable_if_t<!std::is_base_of<std::ios_base, U>::value>>
//std::enable_if_t<!std::is_base_of_v<std::ios_base, U>>> C++17
auto operator>>(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Returns the logical AND of two numbers.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Logical AND of two numbers.
template <Predicate T, Predicate U>
auto operator&&(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Returns the logical AND of two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Logical AND of two numbers.
template <Predicate T, typename U>
auto operator&&(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Returns the logical AND of two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Logical AND of two numbers.
template <Predicate T, typename U>
auto operator&&(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Returns the logical OR of two numbers.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Logical OR of two numbers.
template <Predicate T, Predicate U>
auto operator||(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Returns the logical OR of two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Logical OR of two numbers.
template <Predicate T, typename U>
auto operator||(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Returns the logical OR of two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return Logical OR of two numbers.
template <Predicate T, typename U>
auto operator||(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if numbers are equal, false otherwise.
template <Predicate T, Predicate U>
bool operator==(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if numbers are equal, false otherwise.
template <Predicate T, typename U>
bool operator==(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if numbers are equal, false otherwise.
template <Predicate T, typename U>
bool operator==(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if numbers are not equal, false otherwise.
template <Predicate T, Predicate U>
bool operator!=(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if numbers are not equal, false otherwise.
template <Predicate T, typename U>
bool operator!=(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if numbers are not equal, false otherwise.
template <Predicate T, typename U>
bool operator!=(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if first number is less than second, false otherwise.
template <Predicate T, Predicate U>
bool operator<(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if first number is less than second, false otherwise.
template <Predicate T, typename U>
bool operator<(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if first number is less than second, false otherwise.
template <Predicate T, typename U>
bool operator<(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if first number is greater than second, false otherwise.
template <Predicate T, Predicate U>
bool operator>(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if first number is greater than second, false otherwise.
template <Predicate T, typename U>
bool operator>(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if first number is greater than second, false otherwise.
template <Predicate T, typename U>
bool operator>(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if first number is less than or equal to second, false
/// otherwise.
template <Predicate T, Predicate U>
bool operator<=(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if first number is less than or equal to second, false
/// otherwise.
template <Predicate T, typename U>
bool operator<=(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if first number is less than or equal to second, false
/// otherwise.
template <Predicate T, typename U>
bool operator<=(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if first number is greater than or equal to second, false
/// otherwise.
template <Predicate T, Predicate U>
bool operator>=(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if first number is greater than or equal to second, false
/// otherwise.
template <Predicate T, typename U>
bool operator>=(const CheckedNumber<T>& lhs, const U& rhs);

/// \brief Compares two numbers.
/// \tparam T Predicate of the checked number.
/// \tparam U Type of another number.
/// \param[in] lhs First number.
/// \param[in] rhs Second number.
/// \return True if first number is greater than or equal to second, false
/// otherwise.
template <Predicate T, typename U>
bool operator>=(const U& lhs, const CheckedNumber<T>& rhs);

/// \brief Reads a number from stream.
/// \tparam T Predicate of the checked number.
/// \tparam CharT Character type of the stream.
/// \tparam Traits Traits type of the stream.
/// \param[in,out] stream Stream to read from.
/// \param[out] number Number to write to.
/// \return Reference to stream.
/// \throw std::domain_error If the value read is illegal.
template <Predicate T, typename CharT, typename Traits>
std::basic_istream<CharT, Traits>& operator>>(
    std::basic_istream<CharT, Traits>& stream, CheckedNumber<T>& number);

/// \brief Writes a number to stream.
/// \tparam T Predicate of the checked number.
/// \tparam CharT Character type of the stream.
/// \tparam Traits Traits type of the stream.
/// \param[in,out] stream Stream to write to.
/// \param[in] number Number to read from.
/// \return Reference to stream.
template <Predicate T, typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>& operator<<(
    std::basic_ostream<CharT, Traits>& stream, const CheckedNumber<T>& number);

/// \brief Returns the absolute value of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Absolute value of the underlying value.
template <Predicate T>
auto abs(const CheckedNumber<T>& number);

/// \brief Returns the e raised to the power of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return E raised to the power of the underlying value.
template <Predicate T>
auto exp(const CheckedNumber<T>& number);

/// \brief Returns the natural logarithm of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Natural logarithm of the underlying value.
template <Predicate T>
auto log(const CheckedNumber<T>& number);

/// \brief Returns the base-10 logarithm of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Base-10 logarithm of the underlying value.
template <Predicate T>
auto log10(const CheckedNumber<T>& number);

/// \brief Returns the base raised to the power of the exponent.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] base Base value.
/// \param[in] exponent Exponent value.
/// \return Base raised to the power of the exponent.
template <Predicate T, Predicate U>
auto pow(const CheckedNumber<T>& base, const CheckedNumber<U>& exponent);

/// \brief Returns the base raised to the power of the exponent.
/// \tparam T Predicate of the first checked number.
/// \tparam U Type of another number.
/// \param[in] base Base value.
/// \param[in] exponent Exponent value.
/// \return Base raised to the power of the exponent.
template <Predicate T, typename U>
auto pow(const CheckedNumber<T>& base, const U& exponent);

/// \brief Returns the base raised to the power of the exponent.
/// \tparam T Predicate of the first checked number.
/// \tparam U Type of another number.
/// \param[in] base Base value.
/// \param[in] exponent Exponent value.
/// \return Base raised to the power of the exponent.
template <Predicate T, typename U>
auto pow(const U& base, const CheckedNumber<T>& exponent);

/// \brief Returns the square root of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Square root of the underlying value.
template <Predicate T>
auto sqrt(const CheckedNumber<T>& number);

/// \brief Returns the sine of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Sine of the underlying value.
template <Predicate T>
auto sin(const CheckedNumber<T>& number);

/// \brief Returns the cosine of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Cosine of the underlying value.
template <Predicate T>
auto cos(const CheckedNumber<T>& number);

/// \brief Returns the tangent of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Tangent of the underlying value.
template <Predicate T>
auto tan(const CheckedNumber<T>& number);

/// \brief Returns the Arc sine of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Arc sine of the underlying value.
template <Predicate T>
auto asin(const CheckedNumber<T>& number);

/// \brief Returns the arc cosine of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Arc cosine of the underlying value.
template <Predicate T>
auto acos(const CheckedNumber<T>& number);

/// \brief Returns the arc tangent of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Arc tangent of the underlying value.
template <Predicate T>
auto atan(const CheckedNumber<T>& number);

/// \brief Returns the arc tangent of y / x.
/// \tparam T Predicate of the first checked number.
/// \tparam U Predicate of the second checked number.
/// \param[in] y Y value.
/// \param[in] x X value.
/// \return Arc tangent of y / x.
template <Predicate T, Predicate U>
auto atan2(const CheckedNumber<T>& y, const CheckedNumber<U>& x);

/// \brief Returns the arc tangent of y / x.
/// \tparam T Predicate of the first checked number.
/// \tparam U Type of another number.
/// \param[in] y Y value.
/// \param[in] x X value.
/// \return Arc tangent of y / x.
template <Predicate T, typename U>
auto atan2(const CheckedNumber<T>& y, const U& x);

/// \brief Returns the arc tangent of y / x.
/// \tparam T Predicate of the first checked number.
/// \tparam U Type of another number.
/// \param[in] y Y value.
/// \param[in] x X value.
/// \return Arc tangent of y / x.
template <Predicate T, typename U>
auto atan2(const U& y, const CheckedNumber<T>& x);

/// \brief Returns the hyperbolic sine of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Hyperbolic sine of the underlying value.
template <Predicate T>
auto sinh(const CheckedNumber<T>& number);

/// \brief Returns the hyperbolic cosine of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Hyperbolic cosine of the underlying value.
template <Predicate T>
auto cosh(const CheckedNumber<T>& number);

/// \brief Returns the hyperbolic tangent of the underlying value.
/// \tparam T Predicate of the checked number.
/// \param[in] number Input value.
/// \return Hyperbolic tangent of the underlying value.
template <Predicate T>
auto tanh(const CheckedNumber<T>& number);

}
}

#include "CheckedNumber.hpp"

Definitions:

/// \file
/// \brief Internal header file that contains implementation of the
/// CheckedNumber class template.
/// \author Lyberta
/// \copyright GNU GPLv3 or any later version.

#pragma once

#include <cmath>

namespace ftz
{
namespace General
{

template <typename T>
constexpr CheckedNumber<T>::CheckedNumber(ValueType val)
    : value{std::move(val)}
{
    T::Check(value);
}

template <typename T>
constexpr const typename CheckedNumber<T>::ValueType& CheckedNumber<T>::
    GetValue() const noexcept
{
    return value;
}

template <typename T>
constexpr void CheckedNumber<T>::SetValue(ValueType newvalue)
{
    T::Check(newvalue);
    value = std::move(newvalue);
}

template <typename T>
constexpr CheckedNumber<T>::operator ValueType() const noexcept
{
    return value;
}

template <typename T>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator=(ValueType newvalue)
{
    T::Check(newvalue);
    value = std::move(newvalue);
}

template <typename T>
constexpr CheckedNumber<T>::operator bool() const
{
    return value;
}

template <typename T>
constexpr bool CheckedNumber<T>::operator!() const
{
    return !value;
}

template <typename T>
constexpr auto CheckedNumber<T>::operator+() const
{
    return +value;
}

template <typename T>
constexpr auto CheckedNumber<T>::operator-() const
{
    return -value;
}

template <typename T>
constexpr auto CheckedNumber<T>::operator~() const
{
    return ~value;
}

template <typename T>
CheckedNumber<T>& CheckedNumber<T>::operator++()
{
    ValueType result = value;
    ++result;
    T::Check(result);
    value = std::move(result);
    return *this;
}

template <typename T>
CheckedNumber<T> CheckedNumber<T>::operator++(int)
{
    CheckedNumber<T> savedvalue{value};
    ValueType result = value;
    result++;
    T::Check(result);
    value = std::move(result);
    return savedvalue;
}

template <typename T>
CheckedNumber<T>& CheckedNumber<T>::operator--()
{
    ValueType result = value;
    --result;
    T::Check(result);
    value = std::move(result);
    return *this;
}

template <typename T>
CheckedNumber<T> CheckedNumber<T>::operator--(int)
{
    CheckedNumber<T> savedvalue{value};
    ValueType result = value;
    result--;
    T::Check(result);
    value = std::move(result);
    return savedvalue;
}

template <typename T> template <typename U>
CheckedNumber<T>& CheckedNumber<T>::operator+=(const U& val)
{
    ValueType result = value + val;
    T::Check(result);
    value = std::move(result);
    return *this;
}

template <typename T> template <typename U>
CheckedNumber<T>& CheckedNumber<T>::operator-=(const U& val)
{
    ValueType result = value - val;
    T::Check(result);
    value = std::move(result);
    return *this;
}

template <typename T> template <typename U>
CheckedNumber<T>& CheckedNumber<T>::operator*=(const U& val)
{
    ValueType result = value * val;
    T::Check(result);
    value = std::move(result);
    return *this;
}

template <typename T> template <typename U>
CheckedNumber<T>& CheckedNumber<T>::operator/=(const U& val)
{
    ValueType result = value / val;
    T::Check(result);
    value = std::move(result);
    return *this;
}

template <typename T> template <typename U>
CheckedNumber<T>& CheckedNumber<T>::operator%=(const U& val)
{
    ValueType result = value % val;
    T::Check(result);
    value = std::move(result);
    return *this;
}

template <typename T> template <typename U>
CheckedNumber<T>& CheckedNumber<T>::operator&=(const U& val)
{
    ValueType result = value & val;
    T::Check(result);
    value = std::move(result);
    return *this;
}

template <typename T> template <typename U>
CheckedNumber<T>& CheckedNumber<T>::operator|=(const U& val)
{
    ValueType result = value | val;
    T::Check(result);
    value = std::move(result);
    return *this;
}

template <typename T> template <typename U>
CheckedNumber<T>& CheckedNumber<T>::operator^=(const U& val)
{
    ValueType result = value | val;
    T::Check(result);
    value = std::move(result);
    return *this;
}

template <typename T> template <typename U>
CheckedNumber<T>& CheckedNumber<T>::operator<<=(const U& val)
{
    ValueType result = value << val;
    T::Check(result);
    value = std::move(result);
    return *this;
}

template <typename T> template <typename U>
CheckedNumber<T>& CheckedNumber<T>::operator>>=(const U& val)
{
    ValueType result = value >> val;
    T::Check(result);
    value = std::move(result);
    return *this;
}

template <Predicate T, Predicate U>
auto operator+(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() + rhs.GetValue();
}

template <Predicate T, typename U>
auto operator+(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() + rhs;
}

template <Predicate T, typename U>
auto operator+(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs + rhs.GetValue();
}

template <Predicate T, Predicate U>
auto operator-(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() - rhs.GetValue();
}

template <Predicate T, typename U>
auto operator-(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() - rhs;
}

template <Predicate T, typename U>
auto operator-(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs - rhs.GetValue();
}

template <Predicate T, Predicate U>
auto operator*(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() * rhs.GetValue();
}

template <Predicate T, typename U>
auto operator*(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() * rhs;
}

template <Predicate T, typename U>
auto operator*(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs * rhs.GetValue();
}

template <Predicate T, Predicate U>
auto operator/(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() + rhs.GetValue();
}

template <Predicate T, typename U>
auto operator/(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() / rhs;
}

template <Predicate T, typename U>
auto operator/(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs / rhs.GetValue();
}

template <Predicate T, Predicate U>
auto operator%(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() + rhs.GetValue();
}

template <Predicate T, typename U>
auto operator%(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() + rhs;
}

template <Predicate T, typename U>
auto operator%(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs + rhs.GetValue();
}

template <Predicate T, Predicate U>
auto operator&(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() & rhs.GetValue();
}

template <Predicate T, typename U>
auto operator&(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() & rhs;
}

template <Predicate T, typename U>
auto operator&(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs & rhs.GetValue();
}

template <Predicate T, Predicate U>
auto operator|(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() | rhs.GetValue();
}

template <Predicate T, typename U>
auto operator|(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() | rhs;
}

template <Predicate T, typename U>
auto operator|(const U& lhs,
    const CheckedNumber<T>& rhs)
{
    return lhs | rhs.GetValue();
}

template <Predicate T, Predicate U>
auto operator^(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() ^ rhs.GetValue();
}

template <Predicate T, typename U>
auto operator^(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() ^ rhs;
}

template <Predicate T, typename U>
auto operator^(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs ^ rhs.GetValue();
}

template <Predicate T, Predicate U>
auto operator<<(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() << rhs.GetValue();
}

template <Predicate T, typename U>
auto operator<<(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() << rhs;
}

template <Predicate T, typename U,
std::enable_if_t<!std::is_base_of<std::ios_base, U>::value>>
//std::enable_if_t<!std::is_base_of_v<std::ios_base, U>>> C++17
auto operator<<(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs << rhs.GetValue();
}

template <Predicate T, Predicate U>
auto operator>>(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() >> rhs.GetValue();
}

template <Predicate T, typename U>
auto operator>>(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() >> rhs;
}

template <Predicate T, typename U,
std::enable_if_t<!std::is_base_of<std::ios_base, U>::value>>
//std::enable_if_t<!std::is_base_of_v<std::ios_base, U>>> C++17
auto operator>>(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs >> rhs.GetValue();
}

template <Predicate T, Predicate U>
auto operator&&(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() && rhs.GetValue();
}

template <Predicate T, typename U>
auto operator&&(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() && rhs;
}

template <Predicate T, typename U>
auto operator&&(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs && rhs.GetValue();
}

template <Predicate T, Predicate U>
auto operator||(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() || rhs.GetValue();
}

template <Predicate T, typename U>
auto operator||(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() || rhs;
}

template <Predicate T, typename U>
auto operator||(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs || rhs.GetValue();
}

template <Predicate T, Predicate U>
bool operator==(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() == rhs.GetValue();
}

template <Predicate T, typename U>
bool operator==(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() == rhs;
}

template <Predicate T, typename U>
bool operator==(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs == rhs.GetValue();
}

template <Predicate T, Predicate U>
bool operator!=(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() != rhs.GetValue();
}

template <Predicate T, typename U>
bool operator!=(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() != rhs;
}

template <Predicate T, typename U>
bool operator!=(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs != rhs.GetValue();
}

template <Predicate T, Predicate U>
bool operator<(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() < rhs.GetValue();
}

template <Predicate T, typename U>
bool operator<(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() < rhs;
}

template <Predicate T, typename U>
bool operator<(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs < rhs.GetValue();
}

template <Predicate T, Predicate U>
bool operator>(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() > rhs.GetValue();
}

template <Predicate T, typename U>
bool operator>(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() > rhs;
}

template <Predicate T, typename U>
bool operator>(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs > rhs.GetValue();
}

template <Predicate T, Predicate U>
bool operator<=(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() <= rhs.GetValue();
}

template <Predicate T, typename U>
bool operator<=(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() <= rhs;
}

template <Predicate T, typename U>
bool operator<=(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs <= rhs.GetValue();
}

template <Predicate T, Predicate U>
bool operator>=(const CheckedNumber<T>& lhs, const CheckedNumber<U>& rhs)
{
    return lhs.GetValue() >= rhs.GetValue();
}

template <Predicate T, typename U>
bool operator>=(const CheckedNumber<T>& lhs, const U& rhs)
{
    return lhs.GetValue() >= rhs;
}

template <Predicate T, typename U>
bool operator>=(const U& lhs, const CheckedNumber<T>& rhs)
{
    return lhs >= rhs.GetValue();
}

template <Predicate T, typename CharT, typename Traits>
std::basic_istream<CharT, Traits>& operator>>(
    std::basic_istream<CharT, Traits>& stream, CheckedNumber<T>& number)
{
    typename CheckedNumber<T>::ValueType value;
    stream >> value;
    number = value;
    return stream;
}

template <Predicate T, typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>& operator<<(
    std::basic_ostream<CharT, Traits>& stream, const CheckedNumber<T>& number)
{
    stream << number.GetValue();
    return stream;
}

template <Predicate T>
auto abs(const CheckedNumber<T>& number)
{
    using std::abs;
    return abs(number.GetValue());
}

template <Predicate T, typename U>
auto exp(const CheckedNumber<T>& number, const U& exponent)
{
    using std::exp;
    return exp(number.GetValue(),
        exponent);
}

template <Predicate T>
auto log(const CheckedNumber<T>& number)
{
    using std::log;
    return log(number.GetValue());
}

template <Predicate T>
auto log10(const CheckedNumber<T>& number)
{
    using std::log10;
    return log10(number.GetValue());
}

template <Predicate T, Predicate U>
auto pow(const CheckedNumber<T>& base, const CheckedNumber<U>& exponent)
{
    using std::pow;
    return pow(base.GetValue(), exponent.GetValue());
}

template <Predicate T, typename U>
auto pow(const CheckedNumber<T>& base, const U& exponent)
{
    using std::pow;
    return pow(base.GetValue(), exponent);
}

template <Predicate T, typename U>
auto pow(const U& base, const CheckedNumber<T>& exponent)
{
    using std::pow;
    return pow(base, exponent.GetValue());
}

template <Predicate T>
auto sqrt(const CheckedNumber<T>& number)
{
    using std::sqrt;
    return sqrt(number.GetValue());
}

template <Predicate T>
auto sin(const CheckedNumber<T>& number)
{
    using std::sin;
    return sin(number.GetValue());
}

template <Predicate T>
auto cos(const CheckedNumber<T>& number)
{
    using std::cos;
    return cos(number.GetValue());
}

template <Predicate T>
auto tan(const CheckedNumber<T>& number)
{
    using std::tan;
    return tan(number.GetValue());
}

template <Predicate T>
auto asin(const CheckedNumber<T>& number)
{
    using std::asin;
    return asin(number.GetValue());
}

template <Predicate T>
auto acos(const CheckedNumber<T>& number)
{
    using std::acos;
    return acos(number.GetValue());
}

template <Predicate T>
auto atan(const CheckedNumber<T>& number)
{
    using std::atan;
    return atan(number.GetValue());
}

template <Predicate T, Predicate U>
auto atan2(const CheckedNumber<T>& y, const CheckedNumber<U>& x)
{
    using std::atan2;
    return atan2(y.GetValue(), x.GetValue());
}

template <Predicate T, typename U>
auto atan2(const CheckedNumber<T>& y, const U& x)
{
    using std::atan2;
    return atan2(y.GetValue(), x);
}

template <Predicate T, typename U>
auto atan2(const U& y, const CheckedNumber<T>& x)
{
    using std::atan2;
    return atan2(y, x.GetValue());
}

template <Predicate T>
auto sinh(const CheckedNumber<T>& number)
{
    using std::sinh;
    return abs(number.GetValue());
}

template <Predicate T>
auto cosh(const CheckedNumber<T>& number)
{
    using std::cosh;
    return cosh(number.GetValue());
}

template <Predicate T>
auto atanh(const CheckedNumber<T>& number)
{
    using std::atanh;
    return atanh(number.GetValue());
}

}
}

Now first, I've started marking functions constexpr but quickly found that I don't have much knowledge of constexpr so I don't know which functions should be constexpr. Which ones? It looks like I can mark all functions constexpr.

Second, I haven't added non-operator functions such as abs or sin. I'd want to overload std versions (this is how std::complex and std::valarray do this) but it looks like it is undefined behavior. So I don't know what to do. Suggestions?

EDIT: OK, since I can't overload std functions, I've put them in the same namespace as template so I'd need to use unqualified, say, abs, and rely on ADL. So far, I've added all functions that are declared for std::valarray but it looks like C++11 and 14 added more stuff to <cmath> so I'll add them too.

EDIT2: I've just found that if I derive from this template, I need to import all overloaded operators into the derived class. Is there a better way?

\$\endgroup\$
2
  • \$\begingroup\$ Ok, maybe I read stackoverflow.com/questions/8513417/… totally wrong... \$\endgroup\$
    – user109835
    Feb 4, 2017 at 12:28
  • \$\begingroup\$ On the other hand, I can't partially specialize function templates so that means I can't specialize, say , std::sin for CheckedNumber<T>. \$\endgroup\$
    – user109835
    Feb 4, 2017 at 12:36

1 Answer 1

3
\$\begingroup\$

Headers

For forward declarations of streams, use <iosfwd> in the header - it's more lightweight than requiring <istream> and <ostream>. You'll need the latter two for the implementation, of course.

We're missing <type_traits>, which is required for std::enable_if_t and std::is_base_of. (Aside: why not use std::is_base_of_v instead?). We also need <utility>, for std::move.

Terminology

I expect a "predicate" to return a boolean value. A function that throws should be called an "assertion".

It might also be better if we used the standard operator() for the test, and allow passing a lambda (or indeed, any std::function) as the predicate. Unfortunately, C++14 doesn't allow using a callable as a template parameter, so that would require us to carry a member around with each value.

Don't overload the short-circuiting operators

Overloading operator&& and operator|| can cause surprises for users, as the second argument is always evaluated, unlike with the built-in versions. Instead, allow the arguments to convert to bool in the usual way (remember that an explicit operator bool will be used in this context if necessary).

GetValue and SetValue seem pointless

These two functions add no value (!) to the class, since they just duplicate operator ValueType() and operator=.

Assignment operators should return *this

template <typename T>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator=(ValueType newvalue)
{
    T::Check(newvalue);
    value = std::move(newvalue);
    // oops!
}

Implement copying

In case the predicate is expensive, allow copy construction and assignment. These methods can be noexcept.

Don't implement non-assigning operators

There's no need for operators such as (unary or binary) + and -, or ~, |, etc - we're happy with the standard fallback using the conversion to ValueType. The same goes for the <cmath> functions - they will work just fine with the implicit conversion to the underlying type.

Assigning operators can be noexcept...

... if the underlying type's operator is noexcept. Let's assume that it is, since this is intended for arithmetic types.

Use the assignment operator instead of repeating test code

Consider the implementation of operator+:

{
    ValueType result = value + val;
    T::Check(result);
    value = std::move(result);
    return *this;
}

Most of that merely duplicates assigning from value + val, so re-use that:

{
    return *this = value + val;
}

Write some tests

Without tests, it's not obvious that you've used | instead of ^ in the implementation of operator^=(), for example.


Modified code

I was able to reduce much of the code, without losing any of the functionality:

#include <type_traits>
#include <utility>
#include <istream>

namespace ftz
{
namespace General
{

template <typename T>
concept bool Predicate()
{
    return requires()
    {
        typename T::ValueType;
    } &&
    requires (typename T::ValueType value)
    {
        {T::Check(value)} -> void;
    };
}

/// \brief A checked number.
/// \details Checked number is a number which can't have some values which are
/// otherwise possible for the underlying type. For example, a floating point
/// number which can't be negative. This class takes a predicate class which
/// defines the underlying type of the value and a static function to check the
/// value. Predicate must throw std::domain_error if the value is illegal.
/// \tparam T Type of the predicate.
template<Predicate T>
class CheckedNumber
{
public:
    using ValueType = typename T::ValueType; ///< Underlying type of the value.

    /// \brief Constructor.
    /// \param[in] val Value to set.
    /// \throw std::domain_error If value is illegal.
    constexpr CheckedNumber(ValueType val = ValueType{});

    /// \brief Returns the underlying value.
    /// \return Underlying value.
    constexpr operator ValueType() const noexcept { return value; }

    /// \brief Preincrements the underlying value.
    /// \return Reference to this number.
    /// \throw std::domain_error if result is illegal.
    constexpr CheckedNumber& operator++();

    /// \brief Postincrements the underlying value.
    /// \return Copy of this number before increment.
    /// \throw std::domain_error if the incremented value is illegal.
    constexpr CheckedNumber operator++(int);

    /// \brief Predecrements the underlying value.
    /// \return Reference to this number.
    /// \throw std::domain_error if result is illegal.
    constexpr CheckedNumber& operator--();

    /// \brief Postdecrements the underlying value.
    /// \return Copy of this number before decrement.
    /// \throw std::domain_error if the decremented value is illegal.
    constexpr CheckedNumber operator--(int);

    /// \brief Adds a value to the underlying value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to add.
    /// \return Reference to this number.
    /// \throw std::domain_error If the sum is illegal.
    template <typename U>
    constexpr CheckedNumber& operator+=(const U& val);

    /// \brief Subtracts a value from the underlying value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to subtract.
    /// \return Reference to this number.
    /// \throw std::domain_error If the difference is illegal.
    template <typename U>
    constexpr CheckedNumber& operator-=(const U& val);

    /// \brief Mulptiplies the underlying value by another value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to multiply by.
    /// \return Reference to this number.
    /// \throw std::domain_error If the product is illegal.
    template <typename U>
    constexpr CheckedNumber& operator*=(const U& val);

    /// \brief Divides the underlying value by another value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to divide by.
    /// \return Reference to this number.
    /// \throw std::domain_error If the quotient is illegal.
    template <typename U>
    constexpr CheckedNumber& operator/=(const U& val);

    /// \brief Computes the remainder of the division by another value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to divide by.
    /// \return Reference to this number.
    /// \throw std::domain_error If the remainder is illegal.
    template <typename U>
    constexpr CheckedNumber& operator%=(const U& val);

    /// \brief Computes bitwise AND of underlying value and another value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to compute with.
    /// \return Reference to this number.
    /// \throw std::domain_error If the result is illegal.
    template <typename U>
    constexpr CheckedNumber& operator&=(const U& val);

    /// \brief Computes bitwise OR of underlying value and another value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to compute with.
    /// \return Reference to this number.
    /// \throw std::domain_error If the result is illegal.
    template <typename U>
    constexpr CheckedNumber& operator|=(const U& val);

    /// \brief Computes bitwise XOR of underlying value and another value.
    /// \tparam U Type of the value.
    /// \param[in] val Value to compute with.
    /// \return Reference to this number.
    /// \throw std::domain_error If the result is illegal.
    template <typename U>
    constexpr CheckedNumber& operator^=(const U& val);

    /// \brief Shifts the underlying value by several places to the left.
    /// \tparam U Type of the value.
    /// \param[in] val Value to shift by.
    /// \return Reference to this number.
    /// \throw std::domain_error If the result is illegal.
    template <typename U>
    constexpr CheckedNumber& operator<<=(const U& val);

    /// \brief Shifts the underlying value by several places to the right.
    /// \tparam U Type of the value.
    /// \param[in] val Value to shift by.
    /// \return Reference to this number.
    /// \throw std::domain_error If the result is illegal.
    template <typename U>
    constexpr CheckedNumber& operator>>=(const U& val);

private:
    ValueType value; ///< Underlying value.
};

// Implementations

template <typename T>
constexpr CheckedNumber<T>::CheckedNumber(ValueType val)
    : value{std::move(val)}
{
    T::Check(value);
}

template <typename T>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator++()
{
    auto result = value;
    return *this = ++result;
}

template <typename T>
constexpr CheckedNumber<T> CheckedNumber<T>::operator++(int)
{
    auto const savedvalue = *this;
    auto result = value;
    result++;
    *this = result;
    return savedvalue;
}

template <typename T>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator--()
{
    auto result = value;
    return *this = --result;
}

template <typename T>
constexpr CheckedNumber<T> CheckedNumber<T>::operator--(int)
{
    auto const savedvalue = *this;
    auto result = value;
    result--;
    *this = result;
    return savedvalue;
}

template <typename T> template <typename U>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator+=(const U& val)
{
    return *this = value + val;
}

template <typename T> template <typename U>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator-=(const U& val)
{
    return *this = value - val;
}

template <typename T> template <typename U>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator*=(const U& val)
{
    return *this = value * val;
}

template <typename T> template <typename U>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator/=(const U& val)
{
    return *this = value / val;
}

template <typename T> template <typename U>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator%=(const U& val)
{
    return *this = value % val;
}

template <typename T> template <typename U>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator&=(const U& val)
{
    return *this = value & val;
}

template <typename T> template <typename U>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator|=(const U& val)
{
    return *this = value | val;
}

template <typename T> template <typename U>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator^=(const U& val)
{
    return *this = value ^ val;
}

template <typename T> template <typename U>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator<<=(const U& val)
{
    return *this = value << val;
}

template <typename T> template <typename U>
constexpr CheckedNumber<T>& CheckedNumber<T>::operator>>=(const U& val)
{
    return *this = value >> val;
}


template <Predicate T, typename CharT, typename Traits>
std::basic_istream<CharT, Traits>& operator>>(std::basic_istream<CharT, Traits>& in,
                                              CheckedNumber<T>& number)
{
    typename CheckedNumber<T>::ValueType value;
    if (in >> value) number = value;
    return in;
}



}
}

Here's a start on the tests (using Google Test, but you should be able to adapt to your favourite test runner):

#include <gtest/gtest.h>

#include <cmath>
#include <sstream>
#include <stdexcept>

struct assert_even
{
    using ValueType = int;
    static constexpr void Check(ValueType n) {
        if (n % 2)
            throw std::domain_error("Violated EvenNumber constraint");
    }
};

struct assert_small
{
    using ValueType = char;
    static constexpr void Check(ValueType n) {
        if (n < -9 or 9 < n)
            throw std::domain_error("Violated SmallNumber constraint");
    }
};

struct assert_small_double
{
    using ValueType = double;
    static constexpr void Check(ValueType n) {
        if (n < -9 or 9 < n)
            throw std::domain_error("Violated SmallNumber constraint");
    }
};


using ftz::General::CheckedNumber;

using EvenNumber = CheckedNumber<assert_even>;
using SmallNumber = CheckedNumber<assert_small>;
using SmallDouble = CheckedNumber<assert_small_double>;


TEST(EvenNumber, construct)
{
    EXPECT_NO_THROW({EvenNumber a{0};});
    EXPECT_THROW({EvenNumber a{1};}, std::domain_error);
}

TEST(EvenNumber, istream)
{
    EvenNumber a{6};
    std::istringstream in{"3 2"};
    EXPECT_THROW(in >> a, std::domain_error);
    EXPECT_EQ(a, 6);            // unchanged
    EXPECT_TRUE(in >> a);
    EXPECT_EQ(a, 2);
    EXPECT_FALSE(in >> a);      // end of stream
    EXPECT_EQ(a, 2);            // unchanged
}

TEST(EvenNumber, increment)
{
    EvenNumber a;
    EXPECT_THROW({++a;}, std::domain_error);
    EXPECT_THROW({a++;}, std::domain_error);
    EXPECT_EQ(a, 0);            // strong exception guarantee
}
TEST(EvenNumber, add)
{
    EvenNumber a;
    EXPECT_THROW({a+=1;}, std::domain_error);
    EXPECT_EQ(a+=2, 2);
}
TEST(EvenNumber, false)
{
    EvenNumber a;
    EXPECT_FALSE(a);
}
TEST(EvenNumber, true)
{
    EvenNumber a = 6;
    EXPECT_TRUE(a);
}
TEST(EvenNumber, unary_negate)
{
    EvenNumber a = 6;
    EXPECT_EQ(-a, -6);
}

TEST(SmallNumber, construct)
{
    EXPECT_THROW({SmallNumber a{-10};}, std::domain_error);
    EXPECT_NO_THROW({SmallNumber a{-9};});
    EXPECT_NO_THROW({SmallNumber a{};});
    EXPECT_NO_THROW({SmallNumber a{9};});
    EXPECT_THROW({SmallNumber a{10};}, std::domain_error);
}

TEST(SmallNumber, increment)
{
    SmallNumber a{8};
    EXPECT_EQ(++a, 9);
    EXPECT_THROW({a++;}, std::domain_error);
    EXPECT_EQ(a, 9);            // strong exception guarantee
}

TEST(SmallNumber, multiply)
{
    SmallNumber a{4};
    EXPECT_EQ(a*4, 16);
    EXPECT_THROW({a*=4;}, std::domain_error);
    EXPECT_EQ(a, 4);            // strong exception guarantee
}


TEST(SmallDouble, trigonometric)
{
    SmallDouble a{0};
    EXPECT_DOUBLE_EQ(std::sin(a), 0);
    EXPECT_DOUBLE_EQ(std::cos(a), 1);
}
\$\endgroup\$
4
  • \$\begingroup\$ How are you using concept and constrained template parameters in c++14 ? \$\endgroup\$
    – JDługosz
    Apr 10, 2018 at 11:43
  • \$\begingroup\$ g++-8 -std=c++14 -fPIC -g -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Weffc++ -fconcepts -pthread \$\endgroup\$ Apr 10, 2018 at 11:49
  • \$\begingroup\$ Copy ctor is generated automatically so there is no need to default it. And >> with basic_istream is still required to be manually implemented. Otherwise, everything is good. \$\endgroup\$
    – user109835
    May 22, 2018 at 19:05
  • \$\begingroup\$ Thanks @Lyberta - now fixed. The copy constructor and assignment operator really shouldn't be there, as that inhibits compiler-generated move construct/assign. I've added a stream-in operator and a simple test for it. \$\endgroup\$ May 23, 2018 at 9:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.