#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 #include 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 struct numeric_typedef_base : data { private: using base = opaque::data; 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().operator++()) and std::is_nothrow_constructible::value) { opaque_type r(value); operator++(); return r; } constexpr14 opaque_type operator--(int) & noexcept(noexcept( std::declval().operator++()) and std::is_nothrow_constructible::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(value) )) { return static_cast(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 struct numeric_typedef : numeric_typedef_base , binop::multipliable , binop::dividable , binop::modulable , binop::addable , binop::subtractable , binop::left_shiftable , binop::right_shiftable , binop::bitandable , binop::bitxorable , binop::bitorable { using numeric_typedef_base::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