Skip to content

Instantly share code, notes, and snippets.

@mattgodbolt
Created October 9, 2016 20:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattgodbolt/c0883c94181d1be298b15ba7bc306748 to your computer and use it in GitHub Desktop.
Save mattgodbolt/c0883c94181d1be298b15ba7bc306748 to your computer and use it in GitHub Desktop.
// Copyright (C) 2016 Jonathan Müller <jonathanmueller.dev@gmail.com>
// 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 <jonathanmueller.dev@gmail.com>
//
// 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