Created
October 9, 2016 20:09
-
-
Save mattgodbolt/c0883c94181d1be298b15ba7bc306748 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (C) 2016 Jonathan Müller <[email protected]> | |
// This file is subject to the license terms in the LICENSE file | |
// found in the top-level directory of this distribution. | |
#define TYPE_SAFE_ENABLE_ASSERTIONS 0 // disable assertions | |
//=== assertions boilerplate ===// | |
//======================================================================// | |
// Copyright (C) 2016 Jonathan Müller <[email protected]> | |
// | |
// This software is provided 'as-is', without any express or | |
// implied warranty. In no event will the authors be held | |
// liable for any damages arising from the use of this software. | |
// | |
// Permission is granted to anyone to use this software for any purpose, | |
// including commercial applications, and to alter it and redistribute | |
// it freely, subject to the following restrictions: | |
// | |
// 1. The origin of this software must not be misrepresented; | |
// you must not claim that you wrote the original software. | |
// If you use this software in a product, an acknowledgment | |
// in the product documentation would be appreciated but | |
// is not required. | |
// | |
// 2. Altered source versions must be plainly marked as such, | |
// and must not be misrepresented as being the original software. | |
// | |
// 3. This notice may not be removed or altered from any | |
// source distribution. | |
//======================================================================// | |
#ifndef DEBUG_ASSERT_HPP_INCLUDED | |
#define DEBUG_ASSERT_HPP_INCLUDED | |
#include <cstdlib> | |
#ifndef DEBUG_ASSERT_NO_STDIO | |
#include <cstdio> | |
#endif | |
#ifndef DEBUG_ASSERT_MARK_UNREACHABLE | |
#ifdef __GNUC__ | |
#define DEBUG_ASSERT_MARK_UNREACHABLE __builtin_unreachable() | |
#elif defined(_MSC_VER) | |
#define DEBUG_ASSERT_MARK_UNREACHABLE __assume(0) | |
#else | |
/// Hint to the compiler that a code branch is unreachable. | |
/// Define it yourself prior to including the header to override it. | |
#define DEBUG_ASSERT_MARK_UNREACHABLE | |
#endif | |
#endif | |
#ifndef DEBUG_ASSERT_ASSUME | |
#ifdef __GNUC__ | |
#define DEBUG_ASSERT_ASSUME(Expr) do { if (!(Expr)) __builtin_unreachable(); } while (0) | |
#elif defined(_MSC_VER) | |
#define DEBUG_ASSERT_ASSUME(Expr) __assume(Expr) | |
#else | |
/// Hint to the compiler that a condition is `true`. | |
/// Define it yourself prior to including the header to override it. | |
#define DEBUG_ASSERT_ASSUME(Expr) | |
#endif | |
#endif | |
#ifndef DEBUG_ASSERT_FORCE_INLINE | |
#ifdef __GNUC__ | |
#define DEBUG_ASSERT_FORCE_INLINE [[gnu::always_inline]] inline | |
#elif defined(_MSC_VER) | |
#define DEBUG_ASSERT_FORCE_INLINE __forceinline | |
#else | |
/// Strong hint to the compiler to inline a function. | |
/// Define it yourself prior to including the header to override it. | |
#define DEBUG_ASSERT_FORCE_INLINE inline | |
#endif | |
#endif | |
namespace debug_assert | |
{ | |
//=== source location ===// | |
/// Defines a location in the source code. | |
struct source_location | |
{ | |
const char* file_name; ///< The file name. | |
unsigned line_number; ///< The line number. | |
const char* function_name; ///< The function name. | |
}; | |
/// Expands to the current [debug_assert::source_location](). | |
#define DEBUG_ASSERT_CUR_SOURCE_LOCATION debug_assert::source_location{__FILE__, __LINE__, __func__} | |
//=== level ===// | |
/// Tag type to indicate the level of an assertion. | |
template <unsigned Level> | |
struct level {}; | |
/// Helper class that sets a certain level. | |
/// Inherit from it in your module handler. | |
template <unsigned Level> | |
struct set_level | |
{ | |
static const unsigned level = Level; | |
}; | |
template <unsigned Level> | |
const unsigned set_level<Level>::level; | |
//=== handler ===// | |
/// Does not do anything to handle a failed assertion (except calling [std::abort()]()). | |
/// Inherit from it in your module handler. | |
struct no_handler | |
{ | |
/// \effects Does nothing. | |
/// \notes Can take any additional arguments. | |
template <typename ... Args> | |
static void handle(const source_location&, const char*, Args&&...) noexcept {} | |
}; | |
/// The default handler that writes a message to `stderr`. | |
/// Inherit from it in your module handler. | |
struct default_handler | |
{ | |
/// \effects Prints a message to `stderr`. | |
/// \notes It can optionally accept an additional message string. | |
/// \notes If `DEBUG_ASSERT_NO_STDIO` is defined, it will do nothing. | |
static void handle(const source_location& loc, const char* expression, const char* message = nullptr) noexcept | |
{ | |
#ifndef DEBUG_ASSERT_NO_STDIO | |
if (*expression == '\0') | |
{ | |
if (message) | |
std::fprintf(stderr, | |
"[debug assert] %s:%u: %s: Unreachable code reached - %s.\n", | |
loc.file_name, loc.line_number, loc.function_name, message); | |
else | |
std::fprintf(stderr, | |
"[debug assert] %s:%u: %s: Unreachable code reached.\n", | |
loc.file_name, loc.line_number, loc.function_name); | |
} | |
else if (message) | |
std::fprintf(stderr, | |
"[debug assert] %s:%u: %s: Assertion '%s' failed - %s.\n", | |
loc.file_name, loc.line_number, loc.function_name, | |
expression, message); | |
else | |
std::fprintf(stderr, | |
"[debug assert] %s:%u: %s: Assertion '%s' failed.\n", | |
loc.file_name, loc.line_number, loc.function_name, | |
expression); | |
#else | |
(void)loc; | |
(void)expression; | |
(void)message; | |
#endif | |
} | |
}; | |
/// \exclude | |
namespace detail | |
{ | |
//=== boilerplate ===// | |
// from http://en.cppreference.com/w/cpp/types/remove_reference | |
template <typename T> | |
struct remove_reference | |
{ | |
using type = T; | |
}; | |
template <typename T> | |
struct remove_reference<T&> | |
{ | |
using type = T; | |
}; | |
template <typename T> | |
struct remove_reference<T&&> | |
{ | |
using type = T; | |
}; | |
// from http://stackoverflow.com/a/27501759 | |
template <class T> | |
T&& forward(typename remove_reference<T>::type& t) | |
{ | |
return static_cast<T&&>(t); | |
} | |
template <class T> | |
T&& forward(typename remove_reference<T>::type&& t) | |
{ | |
return static_cast<T&&>(t); | |
} | |
template <bool Value> | |
struct enable_if; | |
template <> | |
struct enable_if<true> | |
{ | |
using type = void; | |
}; | |
template <> | |
struct enable_if<false> {}; | |
//=== assert implementation ===// | |
// use enable if instead of tag dispatching | |
// this removes on additional function and encourage optimization | |
template <class Expr, class Handler, unsigned Level, typename ... Args> | |
auto do_assert(const Expr& expr, const source_location& loc, const char* expression, | |
Handler, level<Level>, | |
Args&&... args) noexcept | |
-> typename enable_if<Level <= Handler::level>::type | |
{ | |
static_assert(Level > 0, "level of an assertion must not be 0"); | |
if (!expr()) | |
{ | |
Handler::handle(loc, expression, forward<Args>(args)...); | |
std::abort(); | |
} | |
} | |
template <class Expr, class Handler, unsigned Level, typename ... Args> | |
DEBUG_ASSERT_FORCE_INLINE | |
auto do_assert(const Expr& expr, const source_location&, const char*, | |
Handler, level<Level>, | |
Args&&...) noexcept | |
-> typename enable_if<(Level > Handler::level)>::type | |
{ | |
DEBUG_ASSERT_ASSUME(expr()); | |
} | |
template <class Expr, class Handler, typename ... Args> | |
auto do_assert(const Expr& expr, const source_location& loc, const char* expression, | |
Handler, | |
Args&&... args) noexcept | |
-> typename enable_if<Handler::level != 0>::type | |
{ | |
if (!expr()) | |
{ | |
Handler::handle(loc, expression, forward<Args>(args)...); | |
std::abort(); | |
} | |
} | |
template <class Expr, class Handler, typename ... Args> | |
DEBUG_ASSERT_FORCE_INLINE | |
auto do_assert(const Expr& expr, const source_location&, const char*, | |
Handler, | |
Args&&...) noexcept | |
-> typename enable_if<Handler::level == 0>::type | |
{ | |
DEBUG_ASSERT_ASSUME(expr()); | |
} | |
} // namespace detail | |
} // namespace debug_assert | |
//=== assertion macros ===// | |
#ifndef DEBUG_ASSERT_DISABLE | |
/// The assertion macro. | |
// | |
/// Usage: `DEBUG_ASSERT(<expr>, <handler>, [<level>], [<handler-specific-args>]. | |
/// Where: | |
/// * `<expr>` - the expression to check for, the expression `!<expr>` must be well-formed and contextually convertible to `bool`. | |
/// * `<handler>` - an object of the module specific handler | |
/// * `<level>` (optional, defaults to `1`) - the level of the assertion, must be an object of type [debug_assert::level<Level>](). | |
/// * `<handler-specific-args>` (optional) - any additional arguments that are just forwarded to the handler function. | |
/// | |
/// It will only check the assertion if `<level>` is less than or equal to `Handler::level`. | |
/// A failed assertion will call: `Handler::handle(location, expression, args)`. | |
/// `location` is the [debug_assert::source_location]() at the macro expansion, | |
/// `expression` is the stringified expression and `args` are the `<handler-specific-args>` as-is. | |
/// If the handler function returns, it will call [std::abort()]. | |
/// | |
/// \notes Define `DEBUG_ASSERT_DISABLE` to completely disable this macro, it will expand to nothing. | |
/// This should not be necessary, the regular version is optimized away completely. | |
#define DEBUG_ASSERT(Expr, ...) \ | |
debug_assert::detail::do_assert([&] { return Expr; }, DEBUG_ASSERT_CUR_SOURCE_LOCATION, #Expr, __VA_ARGS__) | |
/// Marks a branch as unreachable. | |
/// | |
/// Usage: `DEBUG_UNREACHABLE(<handler>, [<level>], [<handler-specific-args>])` | |
/// Where: | |
/// * `<handler>` - an object of the module specific handler | |
/// * `<level>` (optional, defaults to `1`) - the level of the assertion, must be an object of type [debug_assert::level<Level>](). | |
/// * `<handler-specific-args>` (optional) - any additional arguments that are just forwarded to the handler function. | |
/// | |
/// It will only check the assertion if `<level>` is less than or equal to `Handler::level`. | |
/// A failed assertion will call: `Handler::handle(location, "", args)`. | |
/// and `args` are the `<handler-specific-args>` as-is. | |
/// If the handler function returns, it will call [std::abort()]. | |
/// | |
/// \notes Define `DEBUG_ASSERT_DISABLE` to completely disable this macro, it will expand to `DEBUG_ASSERT_MARK_UNREACHABLE`. | |
/// This should not be necessary, the regular version is optimized away completely. | |
#define DEBUG_UNREACHABLE(...) \ | |
debug_assert::detail::do_assert([&] { return false; }, DEBUG_ASSERT_CUR_SOURCE_LOCATION, "", __VA_ARGS__) | |
#else | |
#define DEBUG_ASSERT(Expr, ...) DEBUG_ASSERT_ASSUME(Expr) | |
#define DEBUG_UNREACHABLE(...) DEBUG_ASSERT_MARK_UNREACHABLE | |
#endif | |
#endif // DEBUG_ASSERT_HPP_INCLUDED | |
#define TYPE_SAFE_FORCE_INLINE DEBUG_ASSERT_FORCE_INLINE | |
#ifndef TYPE_SAFE_ENABLE_ASSERTIONS | |
#define TYPE_SAFE_ENABLE_ASSERTIONS 1 | |
#endif | |
namespace type_safe | |
{ | |
namespace detail | |
{ | |
struct assert_handler : debug_assert::set_level<TYPE_SAFE_ENABLE_ASSERTIONS>, | |
debug_assert::default_handler | |
{ | |
}; | |
} // namespace detail | |
} // namespace type_safe | |
//=== end assertions boilerplate ===// | |
#ifndef TYPE_SAFE_INTEGER_HPP_INCLUDED | |
#define TYPE_SAFE_INTEGER_HPP_INCLUDED | |
#include <iosfwd> | |
#include <limits> | |
#include <type_traits> | |
namespace type_safe | |
{ | |
template <typename IntegerT> | |
class integer; | |
/// \exclude | |
namespace detail | |
{ | |
template <typename T> | |
struct is_integer : std::integral_constant<bool, std::is_integral<T>::value | |
&& !std::is_same<T, bool>::value | |
&& !std::is_same<T, char>::value> | |
{ | |
}; | |
template <typename From, typename To> | |
struct is_safe_integer_conversion | |
: std::integral_constant<bool, detail::is_integer<From>::value | |
&& detail::is_integer<To>::value | |
&& sizeof(From) <= sizeof(To) | |
&& std::is_signed<From>::value | |
== std::is_signed<To>::value> | |
{ | |
}; | |
template <typename From, typename To> | |
using enable_safe_integer_conversion = | |
typename std::enable_if<is_safe_integer_conversion<From, To>::value>::type; | |
template <typename From, typename To> | |
using fallback_safe_integer_conversion = | |
typename std::enable_if<!is_safe_integer_conversion<From, To>::value>::type; | |
template <typename A, typename B> | |
struct is_safe_integer_comparision | |
: std::integral_constant<bool, is_safe_integer_conversion<A, B>::value | |
|| is_safe_integer_conversion<B, A>::value> | |
{ | |
}; | |
template <typename A, typename B> | |
using enable_safe_integer_comparision = | |
typename std::enable_if<is_safe_integer_comparision<A, B>::value>::type; | |
template <typename A, typename B> | |
using fallback_safe_integer_comparision = | |
typename std::enable_if<!is_safe_integer_comparision<A, B>::value>::type; | |
template <typename A, typename B> | |
struct is_safe_integer_operation | |
: std::integral_constant<bool, | |
detail::is_integer<A>::value && detail::is_integer<B>::value | |
&& std::is_signed<A>::value == std::is_signed<B>::value> | |
{ | |
}; | |
template <typename A, typename B> | |
struct integer_result_type | |
: std::enable_if<is_safe_integer_operation<A, B>::value, | |
typename std::conditional<sizeof(A) < sizeof(B), B, A>::type> | |
{ | |
}; | |
template <typename A, typename B> | |
using integer_result_t = typename integer_result_type<A, B>::type; | |
template <typename A, typename B> | |
using fallback_integer_result = | |
typename std::enable_if<!is_safe_integer_operation<A, B>::value>::type; | |
template <typename T> | |
constexpr bool will_underflow(T a, T b) | |
{ | |
return !std::is_signed<T>::value && a < b; | |
} | |
template <typename T> | |
constexpr bool will_overflow(T a, T b) | |
{ | |
return !std::is_signed<T>::value && T(a + b) < a; | |
} | |
template <typename T> | |
constexpr bool will_multiplication_overflow(T a, T b) | |
{ | |
return !std::is_signed<T>::value && a != 0 && (T(a * b) / a) != b; | |
} | |
} // namespace detail | |
/// A type safe integer class. | |
/// | |
/// This is a tiny, no overhead wrapper over a standard integer type. | |
/// It behaves exactly like the built-in types except that narrowing conversions are not allowed. | |
/// It also checks against `unsigned` under/overflow in debug mode | |
/// and marks it as undefined for the optimizer otherwise. | |
/// | |
/// A conversion is considered safe if both integer types have the same signedness | |
/// and the size of the value being converted is less than or equal to the destination size. | |
/// | |
/// \requires `IntegerT` must be an integral type except `bool` and `char` (use `signed char`/`unsigned char`). | |
/// \notes It intentionally does not provide the bitwise operations. | |
template <typename IntegerT> | |
class integer | |
{ | |
static_assert(detail::is_integer<IntegerT>::value, "must be a real integer type"); | |
public: | |
using integer_type = IntegerT; | |
//=== constructors ===// | |
template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>> | |
TYPE_SAFE_FORCE_INLINE constexpr integer(const T& val) noexcept : value_(val) | |
{ | |
} | |
template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>> | |
TYPE_SAFE_FORCE_INLINE constexpr integer(const integer<T>& val) noexcept | |
: value_(static_cast<T>(val)) | |
{ | |
} | |
template <typename T, typename = detail::fallback_safe_integer_conversion<T, integer_type>> | |
constexpr integer(T) = delete; | |
//=== assignment ===// | |
template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>> | |
TYPE_SAFE_FORCE_INLINE integer& operator=(const T& val) noexcept | |
{ | |
value_ = val; | |
return *this; | |
} | |
template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>> | |
TYPE_SAFE_FORCE_INLINE integer& operator=(const integer<T>& val) noexcept | |
{ | |
value_ = static_cast<T>(val); | |
return *this; | |
} | |
template <typename T, typename = detail::fallback_safe_integer_conversion<T, integer_type>> | |
integer& operator=(T) = delete; | |
//=== conversion back ===// | |
TYPE_SAFE_FORCE_INLINE explicit constexpr operator integer_type() const noexcept | |
{ | |
return value_; | |
} | |
//=== unary operators ===// | |
TYPE_SAFE_FORCE_INLINE constexpr integer operator+() const noexcept | |
{ | |
return *this; | |
} | |
TYPE_SAFE_FORCE_INLINE constexpr integer operator-() const noexcept | |
{ | |
static_assert(std::is_signed<integer_type>::value, | |
"cannot call unary minus on unsigned integer"); | |
return -value_; | |
} | |
TYPE_SAFE_FORCE_INLINE integer& operator++() noexcept | |
{ | |
DEBUG_ASSERT(!detail::will_overflow(value_, integer_type(1)), detail::assert_handler{}, | |
"overflow detected"); | |
++value_; | |
return *this; | |
} | |
TYPE_SAFE_FORCE_INLINE integer operator++(int)noexcept | |
{ | |
DEBUG_ASSERT(!detail::will_overflow(value_, integer_type(1)), detail::assert_handler{}, | |
"overflow detected"); | |
auto res = *this; | |
++value_; | |
return res; | |
} | |
TYPE_SAFE_FORCE_INLINE integer& operator--() noexcept | |
{ | |
DEBUG_ASSERT(!detail::will_underflow(value_, integer_type(1)), detail::assert_handler{}, | |
"underflow detected"); | |
--value_; | |
return *this; | |
} | |
TYPE_SAFE_FORCE_INLINE integer operator--(int)noexcept | |
{ | |
DEBUG_ASSERT(!detail::will_underflow(value_, integer_type(1)), detail::assert_handler{}, | |
"underflow detected"); | |
auto res = *this; | |
--value_; | |
return res; | |
} | |
//=== compound assignment ====// | |
#define TYPE_SAFE_DETAIL_MAKE_OP(Op) \ | |
template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>> \ | |
TYPE_SAFE_FORCE_INLINE integer& operator Op(const T& other) noexcept \ | |
{ \ | |
return *this Op integer<T>(other); \ | |
} \ | |
template <typename T, typename = detail::fallback_safe_integer_conversion<T, integer_type>> \ | |
integer& operator Op(integer<T>) = delete; \ | |
template <typename T, typename = detail::fallback_safe_integer_conversion<T, integer_type>> \ | |
integer& operator Op(T) = delete; | |
template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>> | |
TYPE_SAFE_FORCE_INLINE integer& operator+=(const integer<T>& other) noexcept | |
{ | |
DEBUG_ASSERT(!detail::will_overflow(value_, static_cast<T>(other)), | |
detail::assert_handler{}, "overflow detected"); | |
value_ += static_cast<T>(other); | |
return *this; | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(+=) | |
template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>> | |
TYPE_SAFE_FORCE_INLINE integer& operator-=(const integer<T>& other) noexcept | |
{ | |
DEBUG_ASSERT(!detail::will_underflow(value_, static_cast<T>(other)), | |
detail::assert_handler{}, "underflow detected"); | |
value_ -= static_cast<T>(other); | |
return *this; | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(-=) | |
template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>> | |
TYPE_SAFE_FORCE_INLINE integer& operator*=(const integer<T>& other) noexcept | |
{ | |
DEBUG_ASSERT(!detail::will_multiplication_overflow(value_, static_cast<T>(other)), | |
detail::assert_handler{}, "overflow detected"); | |
value_ *= static_cast<T>(other); | |
return *this; | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(*=) | |
template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>> | |
TYPE_SAFE_FORCE_INLINE integer& operator/=(const integer<T>& other) noexcept | |
{ | |
value_ /= static_cast<T>(other); | |
return *this; | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(/=) | |
template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>> | |
TYPE_SAFE_FORCE_INLINE integer& operator%=(const integer<T>& other) noexcept | |
{ | |
value_ %= static_cast<T>(other); | |
return *this; | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(%=) | |
#undef TYPE_SAFE_DETAIL_MAKE_OP | |
private: | |
integer_type value_; | |
}; | |
//=== operations ===// | |
/// \exclude | |
namespace detail | |
{ | |
template <typename T> | |
struct make_signed; | |
template <typename T> | |
struct make_signed<integer<T>> | |
{ | |
using type = integer<typename std::make_signed<T>::type>; | |
}; | |
template <typename T> | |
struct make_unsigned; | |
template <typename T> | |
struct make_unsigned<integer<T>> | |
{ | |
using type = integer<typename std::make_unsigned<T>::type>; | |
}; | |
} // namespace detail | |
/// [std::make_signed]() for [type_safe::integer](). | |
template <class Integer> | |
using make_signed_t = typename detail::make_signed<Integer>::type; | |
/// \returns A new [type_safe::integer]() of the corresponding signed integer type. | |
/// \requires The value of `i` must fit into signed type. | |
template <typename Integer> | |
TYPE_SAFE_FORCE_INLINE constexpr make_signed_t<integer<Integer>> make_signed( | |
const integer<Integer>& i) noexcept | |
{ | |
using result_type = typename std::make_signed<Integer>::type; | |
return i <= static_cast<Integer>(std::numeric_limits<result_type>::max()) ? | |
integer<result_type>(static_cast<result_type>(static_cast<Integer>(i))) : | |
(DEBUG_UNREACHABLE(detail::assert_handler{}, "conversion " | |
"would " | |
"overflow"), | |
result_type()); | |
} | |
/// [std::make_unsigned]() for [type_safe::integer](). | |
template <class Integer> | |
using make_unsigned_t = typename detail::make_unsigned<Integer>::type; | |
/// \returns A new [type_safe::integer]() of the corresponding unsigned integer type. | |
/// \requires The value of `i` must not be negative. | |
template <typename Integer> | |
TYPE_SAFE_FORCE_INLINE constexpr make_unsigned_t<integer<Integer>> make_unsigned( | |
const integer<Integer>& i) noexcept | |
{ | |
using result_type = typename std::make_unsigned<Integer>::type; | |
return i >= Integer(0) ? | |
integer<result_type>(static_cast<result_type>(static_cast<Integer>(i))) : | |
(DEBUG_UNREACHABLE(detail::assert_handler{}, "conversion would underflow"), | |
result_type(0)); | |
} | |
/// \returns The absolute value of an [type_safe::integer](). | |
/// \unique_name type_safe::abs-signed | |
template <typename SignedInteger, | |
typename = typename std::enable_if<std::is_signed<SignedInteger>::value>::type> | |
TYPE_SAFE_FORCE_INLINE constexpr make_unsigned_t<integer<SignedInteger>> abs( | |
const integer<SignedInteger>& i) noexcept | |
{ | |
return make_unsigned(i > 0 ? i : -i); | |
} | |
/// \returns `i` unchanged. | |
/// \notes This is an optimization of [type_safe::abs-signed]() for `unsigned` [type_safe::integer](). | |
/// \unique_name type_safe::abs-unsigned | |
template <typename UnsignedInteger, | |
typename = typename std::enable_if<std::is_unsigned<UnsignedInteger>::value>::type> | |
TYPE_SAFE_FORCE_INLINE constexpr integer<UnsignedInteger> abs( | |
const integer<UnsignedInteger>& i) noexcept | |
{ | |
return i; | |
} | |
//=== comparision ===// | |
#define TYPE_SAFE_DETAIL_MAKE_OP(Op) \ | |
template <typename A, typename B, typename = detail::enable_safe_integer_conversion<A, B>> \ | |
TYPE_SAFE_FORCE_INLINE constexpr bool operator Op(const A& a, const integer<B>& b) \ | |
{ \ | |
return integer<A>(a) Op b; \ | |
} \ | |
template <typename A, typename B, typename = detail::enable_safe_integer_conversion<A, B>> \ | |
TYPE_SAFE_FORCE_INLINE constexpr bool operator Op(const integer<A>& a, const B& b) \ | |
{ \ | |
return a Op integer<B>(b); \ | |
} \ | |
template <typename A, typename B, typename = detail::fallback_safe_integer_comparision<A, B>> \ | |
constexpr bool operator Op(integer<A>, integer<B>) = delete; \ | |
template <typename A, typename B, typename = detail::fallback_safe_integer_comparision<A, B>> \ | |
constexpr bool operator Op(A, integer<B>) = delete; \ | |
template <typename A, typename B, typename = detail::fallback_safe_integer_comparision<A, B>> \ | |
constexpr bool operator Op(integer<A>, B) = delete; | |
template <typename A, typename B, typename = detail::enable_safe_integer_comparision<A, B>> | |
TYPE_SAFE_FORCE_INLINE constexpr bool operator==(const integer<A>& a, | |
const integer<B>& b) noexcept | |
{ | |
return static_cast<A>(a) == static_cast<B>(b); | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(==) | |
template <typename A, typename B, typename = detail::enable_safe_integer_comparision<A, B>> | |
TYPE_SAFE_FORCE_INLINE constexpr bool operator!=(const integer<A>& a, | |
const integer<B>& b) noexcept | |
{ | |
return static_cast<A>(a) != static_cast<B>(b); | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(!=) | |
template <typename A, typename B, typename = detail::enable_safe_integer_comparision<A, B>> | |
TYPE_SAFE_FORCE_INLINE constexpr bool operator<(const integer<A>& a, | |
const integer<B>& b) noexcept | |
{ | |
return static_cast<A>(a) < static_cast<B>(b); | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(<) | |
template <typename A, typename B, typename = detail::enable_safe_integer_comparision<A, B>> | |
TYPE_SAFE_FORCE_INLINE constexpr bool operator<=(const integer<A>& a, | |
const integer<B>& b) noexcept | |
{ | |
return static_cast<A>(a) <= static_cast<B>(b); | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(<=) | |
template <typename A, typename B, typename = detail::enable_safe_integer_comparision<A, B>> | |
TYPE_SAFE_FORCE_INLINE constexpr bool operator>(const integer<A>& a, | |
const integer<B>& b) noexcept | |
{ | |
return static_cast<A>(a) > static_cast<B>(b); | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(>) | |
template <typename A, typename B, typename = detail::enable_safe_integer_comparision<A, B>> | |
TYPE_SAFE_FORCE_INLINE constexpr bool operator>=(const integer<A>& a, | |
const integer<B>& b) noexcept | |
{ | |
return static_cast<A>(a) >= static_cast<B>(b); | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(>=) | |
#undef TYPE_SAFE_DETAIL_MAKE_OP | |
//=== binary operations ===// | |
#define TYPE_SAFE_DETAIL_MAKE_OP(Op) \ | |
template <typename A, typename B> \ | |
TYPE_SAFE_FORCE_INLINE constexpr auto operator Op(const A& a, const integer<B>& b) noexcept \ | |
->integer<detail::integer_result_t<A, B>> \ | |
{ \ | |
return integer<A>(a) Op b; \ | |
} \ | |
template <typename A, typename B> \ | |
TYPE_SAFE_FORCE_INLINE constexpr auto operator Op(const integer<A>& a, const B& b) noexcept \ | |
->integer<detail::integer_result_t<A, B>> \ | |
{ \ | |
return a Op integer<B>(b); \ | |
} \ | |
template <typename A, typename B, typename = detail::fallback_integer_result<A, B>> \ | |
constexpr int operator Op(integer<A>, integer<B>) noexcept = delete; \ | |
template <typename A, typename B, typename = detail::fallback_integer_result<A, B>> \ | |
constexpr int operator Op(A, integer<B>) noexcept = delete; \ | |
template <typename A, typename B, typename = detail::fallback_integer_result<A, B>> \ | |
constexpr int operator Op(integer<A>, B) noexcept = delete; | |
template <typename A, typename B> | |
TYPE_SAFE_FORCE_INLINE constexpr auto operator+(const integer<A>& a, | |
const integer<B>& b) noexcept | |
-> integer<detail::integer_result_t<A, B>> | |
{ | |
return detail::will_overflow<detail::integer_result_t<A, B>>(static_cast<A>(a), | |
static_cast<B>(b)) ? | |
(DEBUG_UNREACHABLE(detail::assert_handler{}, "overflow detected"), A()) : | |
detail::integer_result_t<A, B>(static_cast<A>(a) + static_cast<B>(b)); | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(+) | |
template <typename A, typename B> | |
TYPE_SAFE_FORCE_INLINE constexpr auto operator-(const integer<A>& a, | |
const integer<B>& b) noexcept | |
-> integer<detail::integer_result_t<A, B>> | |
{ | |
return detail::will_underflow<detail::integer_result_t<A, B>>(static_cast<A>(a), | |
static_cast<B>(b)) ? | |
(DEBUG_UNREACHABLE(detail::assert_handler{}, "underflow detected"), A()) : | |
detail::integer_result_t<A, B>(static_cast<A>(a) - static_cast<B>(b)); | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(-) | |
template <typename A, typename B> | |
TYPE_SAFE_FORCE_INLINE constexpr auto operator*(const integer<A>& a, | |
const integer<B>& b) noexcept | |
-> integer<detail::integer_result_t<A, B>> | |
{ | |
return detail::will_multiplication_overflow<detail::integer_result_t<A, B>>(static_cast<A>( | |
a), | |
static_cast<B>( | |
b)) ? | |
(DEBUG_UNREACHABLE(detail::assert_handler{}, "overflow detected"), A()) : | |
detail::integer_result_t<A, B>(static_cast<A>(a) * static_cast<B>(b)); | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(*) | |
template <typename A, typename B> | |
TYPE_SAFE_FORCE_INLINE constexpr auto operator/(const integer<A>& a, | |
const integer<B>& b) noexcept | |
-> integer<detail::integer_result_t<A, B>> | |
{ | |
return detail::integer_result_t<A, B>(static_cast<A>(a) / static_cast<B>(b)); | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(/) | |
template <typename A, typename B> | |
TYPE_SAFE_FORCE_INLINE constexpr auto operator%(const integer<A>& a, | |
const integer<B>& b) noexcept | |
-> integer<detail::integer_result_t<A, B>> | |
{ | |
return detail::integer_result_t<A, B>(static_cast<A>(a) % static_cast<B>(b)); | |
} | |
TYPE_SAFE_DETAIL_MAKE_OP(%) | |
#undef TYPE_SAFE_DETAIL_MAKE_OP | |
//=== input/output ===/ | |
template <typename Char, class CharTraits, typename IntegerT> | |
std::basic_istream<Char, CharTraits>& operator>>(std::basic_istream<Char, CharTraits>& in, | |
integer<IntegerT>& i) | |
{ | |
IntegerT val; | |
in >> val; | |
i = val; | |
return in; | |
} | |
template <typename Char, class CharTraits, typename IntegerT> | |
std::basic_ostream<Char, CharTraits>& operator<<(std::basic_ostream<Char, CharTraits>& out, | |
const integer<IntegerT>& i) | |
{ | |
return out << static_cast<IntegerT>(i); | |
} | |
} // namespace type_safe | |
#endif // TYPE_SAFE_INTEGER_HPP_INCLUDED |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment