numeric_typedef.hpp
260 lines
| 10.0 KiB
| text/x-c++hdr
|
CppLexer
r4 | #ifndef OPAQUE_NUMERIC_TYPEDEF_HPP | |||
#define OPAQUE_NUMERIC_TYPEDEF_HPP | ||||
// | ||||
// Copyright (c) 2015, 2016 | ||||
// Kyle Markley. All rights reserved. | ||||
// | ||||
// Redistribution and use in source and binary forms, with or without | ||||
// modification, are permitted provided that the following conditions are met: | ||||
// | ||||
// 1. Redistributions of source code must retain the above copyright notice, | ||||
// this list of conditions and the following disclaimer. | ||||
// 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
// this list of conditions and the following disclaimer in the documentation | ||||
// and/or other materials provided with the distribution. | ||||
// 3. Neither the name of the author nor the names of any contributors may be | ||||
// used to endorse or promote products derived from this software without | ||||
// specific prior written permission. | ||||
// | ||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
// POSSIBILITY OF SUCH DAMAGE. | ||||
// | ||||
#include "binop/binop_inherit.hpp" | ||||
#include "data.hpp" | ||||
#include <type_traits> | ||||
#include <utility> | ||||
namespace opaque { | ||||
/// \addtogroup typedefs | ||||
/// @{ | ||||
/// | ||||
/// Numeric opaque typedef base type | ||||
/// | ||||
/// Same as numeric_typedef, but without providing operator@ in terms of | ||||
/// operator@= by default. (Deriving from this rather than numeric_typedef | ||||
/// can avoid the need to remove a provided operator@ via a template | ||||
/// specialization.) | ||||
/// | ||||
/// This is a suitable base class when you want to control the interface of | ||||
/// your type, including its interoperability with itself and other types. | ||||
/// Delete operations that you do not want, and add operations that you do | ||||
/// want. | ||||
/// | ||||
/// You may define the behavior of mixed-type operator@ in your subclass by | ||||
/// inheriting from the opaque::binop::opname classes and providing the type | ||||
/// details. The list of template arguments for these classes is: | ||||
/// -# return type | ||||
/// -# commutative (bool) | ||||
/// -# type of left operand | ||||
/// -# type of right operand | ||||
/// -# type to convert left operand to | ||||
/// -# type to convert right operand to | ||||
/// | ||||
/// If you do not desire certain standard numeric operations, simply delete | ||||
/// them in your subclass. (Note that it is simpler to delete an unwanted | ||||
/// operation than to supply a missing one that is desired.) | ||||
/// | ||||
template <typename U, typename O, typename S = unsigned> | ||||
struct numeric_typedef_base : data<U,O> { | ||||
private: | ||||
using base = opaque::data<U,O>; | ||||
public: | ||||
using typename base::underlying_type; | ||||
using typename base::opaque_type; | ||||
typedef S shift_type; | ||||
using base::value; | ||||
constexpr14 opaque_type& operator*=(const opaque_type& peer) & | ||||
noexcept(noexcept( value *= peer.value )) { | ||||
value *= peer.value; | ||||
return downcast(); } | ||||
constexpr14 opaque_type& operator/=(const opaque_type& peer) & | ||||
noexcept(noexcept( value /= peer.value )) { | ||||
value /= peer.value; | ||||
return downcast(); } | ||||
constexpr14 opaque_type& operator%=(const opaque_type& peer) & | ||||
noexcept(noexcept( value %= peer.value )) { | ||||
value %= peer.value; | ||||
return downcast(); } | ||||
constexpr14 opaque_type& operator+=(const opaque_type& peer) & | ||||
noexcept(noexcept( value += peer.value )) { | ||||
value += peer.value; | ||||
return downcast(); } | ||||
constexpr14 opaque_type& operator-=(const opaque_type& peer) & | ||||
noexcept(noexcept( value -= peer.value )) { | ||||
value -= peer.value; | ||||
return downcast(); } | ||||
constexpr14 opaque_type& operator<<=(const shift_type& count) & | ||||
noexcept(noexcept( value <<= count )) { | ||||
value <<= count; | ||||
return downcast(); } | ||||
constexpr14 opaque_type& operator>>=(const shift_type& count) & | ||||
noexcept(noexcept( value >>= count )) { | ||||
value >>= count; | ||||
return downcast(); } | ||||
constexpr14 opaque_type& operator&=(const opaque_type& peer) & | ||||
noexcept(noexcept( value &= peer.value )) { | ||||
value &= peer.value; | ||||
return downcast(); } | ||||
constexpr14 opaque_type& operator^=(const opaque_type& peer) & | ||||
noexcept(noexcept( value ^= peer.value )) { | ||||
value ^= peer.value; | ||||
return downcast(); } | ||||
constexpr14 opaque_type& operator|=(const opaque_type& peer) & | ||||
noexcept(noexcept( value |= peer.value )) { | ||||
value |= peer.value; | ||||
return downcast(); } | ||||
constexpr14 opaque_type& operator++() & | ||||
noexcept(noexcept( ++value )) { | ||||
++value; | ||||
return downcast(); } | ||||
constexpr14 opaque_type& operator--() & | ||||
noexcept(noexcept( --value )) { | ||||
--value; | ||||
return downcast(); } | ||||
constexpr14 opaque_type operator++(int) & noexcept(noexcept( | ||||
std::declval<numeric_typedef_base&>().operator++()) and | ||||
std::is_nothrow_constructible<opaque_type, underlying_type>::value) { | ||||
opaque_type r(value); operator++(); return r; } | ||||
constexpr14 opaque_type operator--(int) & noexcept(noexcept( | ||||
std::declval<numeric_typedef_base&>().operator++()) and | ||||
std::is_nothrow_constructible<opaque_type, underlying_type>::value) { | ||||
opaque_type r(value); operator--(); return r; } | ||||
constexpr opaque_type operator+() const & | ||||
noexcept(noexcept( opaque_type(+ value ) )) { | ||||
return opaque_type(+ value ); } | ||||
constexpr14 opaque_type operator+() && | ||||
noexcept(noexcept( opaque_type(+opaque::move(value)) )) { | ||||
return opaque_type(+opaque::move(value)); } | ||||
constexpr opaque_type operator-() const & | ||||
noexcept(noexcept( opaque_type(- value ) )) { | ||||
return opaque_type(- value ); } | ||||
constexpr14 opaque_type operator-() && | ||||
noexcept(noexcept( opaque_type(-opaque::move(value)) )) { | ||||
return opaque_type(-opaque::move(value)); } | ||||
constexpr opaque_type operator~() const & | ||||
noexcept(noexcept( opaque_type(~ value ) )) { | ||||
return opaque_type(~ value ); } | ||||
constexpr14 opaque_type operator~() && | ||||
noexcept(noexcept( opaque_type(~opaque::move(value)) )) { | ||||
return opaque_type(~opaque::move(value)); } | ||||
constexpr bool operator!() const | ||||
noexcept(noexcept( !value )) { | ||||
return !value; } | ||||
constexpr bool operator==(const opaque_type& peer) const | ||||
noexcept(noexcept( value == peer.value )) { | ||||
return value == peer.value; } | ||||
constexpr bool operator!=(const opaque_type& peer) const | ||||
noexcept(noexcept( value != peer.value )) { | ||||
return value != peer.value; } | ||||
constexpr bool operator< (const opaque_type& peer) const | ||||
noexcept(noexcept( value < peer.value )) { | ||||
return value < peer.value; } | ||||
constexpr bool operator> (const opaque_type& peer) const | ||||
noexcept(noexcept( value > peer.value )) { | ||||
return value > peer.value; } | ||||
constexpr bool operator<=(const opaque_type& peer) const | ||||
noexcept(noexcept( value <= peer.value )) { | ||||
return value <= peer.value; } | ||||
constexpr bool operator>=(const opaque_type& peer) const | ||||
noexcept(noexcept( value >= peer.value )) { | ||||
return value >= peer.value; } | ||||
/// Check whether the underlying value is nonzero | ||||
explicit constexpr operator bool() const | ||||
noexcept(noexcept( static_cast<bool>(value) )) { | ||||
return static_cast<bool>(value); } | ||||
using base::base; | ||||
explicit numeric_typedef_base() = default; | ||||
numeric_typedef_base(const numeric_typedef_base& ) = default; | ||||
numeric_typedef_base( numeric_typedef_base&&) = default; | ||||
numeric_typedef_base& operator=(const numeric_typedef_base& ) & = default; | ||||
numeric_typedef_base& operator=( numeric_typedef_base&&) & = default; | ||||
protected: | ||||
~numeric_typedef_base() = default; | ||||
using base::downcast; | ||||
}; | ||||
/// | ||||
/// Numeric opaque typedef | ||||
/// | ||||
/// This is a base class providing wrappers for numeric operations on a | ||||
/// user-supplied type, which may be user-defined or built-in. The purpose of | ||||
/// this type is to easily enable the creation of several rigorously separate | ||||
/// numeric types so that they cannot be accidentally mixed in expressions, | ||||
/// implicitly converted, and also to enable overloading. | ||||
/// | ||||
/// For example, you could create separate types for coordinates in each of | ||||
/// several dimensions, and it would be a compile-time error to mix those types | ||||
/// in a single expression, even if the the same underlying type was used to | ||||
/// represent all of them. | ||||
/// | ||||
/// Template arguments for numeric_typedef: | ||||
/// -# U : The underlying type holding the value | ||||
/// -# O : The opaque type, your subclass | ||||
/// -# S : The right-hand operand type for shift operations | ||||
/// | ||||
template <typename U, typename O, typename S = unsigned> | ||||
struct numeric_typedef : numeric_typedef_base<U,O,S> | ||||
, binop::multipliable <O> | ||||
, binop::dividable <O> | ||||
, binop::modulable <O> | ||||
, binop::addable <O> | ||||
, binop::subtractable <O> | ||||
, binop::left_shiftable <O, false, O, S> | ||||
, binop::right_shiftable<O, false, O, S> | ||||
, binop::bitandable <O> | ||||
, binop::bitxorable <O> | ||||
, binop::bitorable <O> | ||||
{ | ||||
using numeric_typedef_base<U,O,S>::numeric_typedef_base; | ||||
explicit numeric_typedef() = default; | ||||
numeric_typedef(const numeric_typedef& ) = default; | ||||
numeric_typedef( numeric_typedef&&) = default; | ||||
numeric_typedef& operator=(const numeric_typedef& ) & = default; | ||||
numeric_typedef& operator=( numeric_typedef&&) & = default; | ||||
protected: | ||||
~numeric_typedef() = default; | ||||
}; | ||||
/// @} | ||||
} | ||||
#endif | ||||