/*
Copyright (C) 2015-2018 Federico Kircheis
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#ifndef SAFEOPERATIONS_CMP_H
#define SAFEOPERATIONS_CMP_H
#include "errors.hpp"
#include
#include
#include
namespace safeintegralop {
// All functions in the namespace "details" are for private use, you should use all the function outside of this namespace
namespace details{
// could use the same implementation of in_range_signed_signed, but compiler may generate warning that t is always bigger than 0
template
constexpr bool in_range_unsigned_unsigned(const T t) noexcept {
SAFE_INTEGRAL_OP_ASSERT_INTEGRALS_NOT_BOOL_CHAR_TYPE(T,R);
return (std::numeric_limits::digits > std::numeric_limits::digits) ?
(t <= static_cast(std::numeric_limits::max())) :
(static_cast(t) <= std::numeric_limits::max());
}
template
constexpr bool in_range_signed_signed(const T t) noexcept {
SAFE_INTEGRAL_OP_ASSERT_INTEGRALS_NOT_BOOL_CHAR_TYPE(T,R);
return (std::numeric_limits::digits > std::numeric_limits::digits) ?
(t <= static_cast(std::numeric_limits::max()) && t >= static_cast(std::numeric_limits::min())) :
(static_cast(t) <= std::numeric_limits::max() && static_cast(t) >= std::numeric_limits::min());
}
template
constexpr bool in_range_signed_unsigned(const T t) noexcept {
SAFE_INTEGRAL_OP_ASSERT_INTEGRALS_NOT_BOOL_CHAR_TYPE(T,R);
return (t < T{ 0 }) ? false :
(std::numeric_limits::digits / 2 <= std::numeric_limits::digits) ? true :
(t <= static_cast(std::numeric_limits::max()));
}
template
constexpr bool in_range_unsigned_signed(const T t) noexcept {
SAFE_INTEGRAL_OP_ASSERT_INTEGRALS_NOT_BOOL_CHAR_TYPE(T,R);
return (std::numeric_limits::digits >= std::numeric_limits::digits / 2) ? (t <= static_cast(std::numeric_limits::max())) : true;
}
template
constexpr bool in_range_unsigned(const T t) noexcept {
return std::is_unsigned::value ? in_range_unsigned_unsigned(t) : in_range_unsigned_signed(t);
}
template
constexpr bool in_range_signed(const T t) noexcept {
return std::is_signed::value ? in_range_signed_signed(t) : in_range_signed_unsigned(t);
}
// equivalent of operator== for different integral types
template
constexpr bool cmp_equal_same_sign(const T t, const U u) noexcept {
SAFE_INTEGRAL_OP_ASSERT_INTEGRALS_NOT_BOOL_CHAR_TYPE(T,U);
return (std::numeric_limits::digits>std::numeric_limits::digits) ? (t == static_cast(u)) : (static_cast(t) == u);
}
template
constexpr bool cmp_equal_signed_unsigned(const T t, const U u) noexcept {
SAFE_INTEGRAL_OP_ASSERT_INTEGRALS_NOT_BOOL_CHAR_TYPE(T,U);
return (t::digits / 2>std::numeric_limits::digits) ? (t == static_cast(u)) : (static_cast(t) == u);
}
// equivalent of operator< for different integral types
template
constexpr bool cmp_less_same_sign(const T t, const U u) noexcept {
SAFE_INTEGRAL_OP_ASSERT_INTEGRALS_NOT_BOOL_CHAR_TYPE(T,U);
return (std::numeric_limits::digits>std::numeric_limits::digits) ? (t < static_cast(u)) : (static_cast(t) < u);
}
template
constexpr bool cmp_less_signed_unsigned(const T t, const U u) noexcept {
SAFE_INTEGRAL_OP_ASSERT_INTEGRALS_NOT_BOOL_CHAR_TYPE(T,U);
return (t::digits / 2>std::numeric_limits::digits) ? (t < static_cast(u)) : (static_cast(t) < u);
}
template
constexpr bool cmp_less_unsigned_signed(const T t, const U u) noexcept {
SAFE_INTEGRAL_OP_ASSERT_INTEGRALS_NOT_BOOL_CHAR_TYPE(T,U);
return (u::digits / 2>std::numeric_limits::digits) ? (static_cast(t) < u) : (t < static_cast(u));
}
} // end details
/// Usage:
/// size_t i == ...
/// if(in_range(i)){
/// safe to use i as a DWORD value, parameter...
/// } else {
/// not possible to rappresent i as a DWORD
/// }
template
constexpr bool in_range(const T t) noexcept {
return std::is_unsigned::value ? details::in_range_unsigned(t) : details::in_range_signed(t);
}
// equivalent of operator== for different types
/// Usage:
/// size_t i == ...
/// DWORD j == ...
/// if(cmp_equal(i,j)){
/// i and j rappresent the same quantity
/// } else {
/// i and j rappresents different quantities
/// }
template
constexpr bool cmp_equal(const T t, const U u) noexcept {
return
(std::is_signed::value == std::is_signed::value) ? details::cmp_equal_same_sign(t, u) :
(std::is_signed::value) ? details::cmp_equal_signed_unsigned(t, u) : details::cmp_equal_signed_unsigned(u,t);
}
// equivalent of operator< for different integral types
/// Usage:
/// size_t i == ...
/// DWORD j == ...
/// if(cmp_less(i,j)){
/// i < j
/// } else {
/// i >= j
/// }
template
constexpr bool cmp_less(const T t, const U u) noexcept {
return
(std::is_signed::value == std::is_signed::value) ? details::cmp_less_same_sign(t,u) :
(std::is_signed::value) ? details::cmp_less_signed_unsigned(t, u) : details::cmp_less_unsigned_signed(t, u);
}
template
constexpr bool cmp_less_eq(const T t, const U u) noexcept {
return cmp_less(t,u) || cmp_equal(t,u);
}
template
constexpr bool cmp_great(const T t, const U u) noexcept {
return cmp_less(u,t);
}
namespace ct {
// Compile tests for in_range
static_assert(in_range(1), "in range");
static_assert(in_range(1u), "in range");
static_assert(in_range(1ul), "in range");
static_assert(in_range(-1l), "in range");
static_assert(!in_range(std::numeric_limits::max()), "in range");
static_assert(in_range(std::numeric_limits::max()), "in range");
static_assert(!in_range(-1), "in range, negative value, unsigned range");
static_assert(in_range(-1), "in range, negative value, unsigned range");
static_assert(in_range(std::numeric_limits::min()), "in range");
static_assert(in_range(std::numeric_limits::min()), "in range");
static_assert(!in_range(std::numeric_limits::min()), "in range");
static_assert(!in_range(std::numeric_limits::min()), "in range");
// Compile tests for cmp_equal
static_assert(cmp_equal(1, 1), "comparison same signed type, same value");
static_assert(cmp_equal(1u, 1u), "comparison same unsigned type, same value (2)");
static_assert(cmp_equal(1ul, 1u), "comparison unsigned types, same value");
static_assert(cmp_equal(1u, 1ul), "comparison unsigned types, same value (2)");
static_assert(cmp_equal(1l, 1), "comparison signed types, same value");
static_assert(cmp_equal(1, 1l), "comparison signed types, same value (2)");
static_assert(cmp_equal(1ul, 1), "comparison signed/unsigned types, same value");
static_assert(cmp_equal(1, 1ul), "comparison signed/unsigned types, same value (2)");
static_assert(!cmp_equal(1, 2), "comparison same signed type, different values");
static_assert(!cmp_equal(1, -1), "comparison same signed type, different values (2)");
static_assert(!cmp_equal(1u, 2u), "comparison same unsigned type, different values");
static_assert(!cmp_equal(1ul, 2u), "comparison unsigned types, different values");
static_assert(!cmp_equal(2u, 1ul), "comparison unsigned types, different values (2)");
static_assert(!cmp_equal(1l, 2), "comparison signed types, different values");
static_assert(!cmp_equal(2, 1l), "comparison signed types, different values (2)");
static_assert(!cmp_equal(2ul, 1), "comparison signed/unsigned types, different values");
static_assert(!cmp_equal(1, 2ul), "comparison signed/unsigned types, different values (2)");
static_assert(!cmp_equal(std::numeric_limits::max(), std::numeric_limits::max()),
"comparison unsigned/signed type");
static_assert(!cmp_equal(std::numeric_limits::max(), -1),
"comparison unsigned/signed type");
// Compile tests for cmp_less
static_assert(!cmp_less(1, 1), "comparison same signed type, same value");
static_assert(!cmp_less(1u, 1u), "comparison same unsigned type, same value (2)");
static_assert(!cmp_less(1ul, 1u), "comparison unsigned types, same value");
static_assert(!cmp_less(1u, 1ul), "comparison unsigned types, same value (2)");
static_assert(!cmp_less(1l, 1), "comparison signed types, same value");
static_assert(!cmp_less(1, 1l), "comparison signed types, same value (2)");
static_assert(!cmp_less(1ul, 1), "comparison signed/unsigned types, same value");
static_assert(!cmp_less(1, 1ul), "comparison signed/unsigned types, same value (2)");
static_assert(cmp_less(1, 2), "comparison same signed type, different values");
static_assert(!cmp_less(1, -1), "comparison same signed type, different values (2)");
static_assert(cmp_less(1u, 2u), "comparison same unsigned type, different values");
static_assert(cmp_less(1ul, 2u) ,"comparison unsigned types, different values");
static_assert(!cmp_less(2u, 1ul),"comparison unsigned types, different values (2)");
static_assert(cmp_less(1l, 2), "comparison signed types, different values");
static_assert(!cmp_less(2, 1l), "comparison signed types, different values (2)");
static_assert(cmp_less(1ul, 2), "comparison signed/unsigned types, different values");
static_assert(!cmp_less(2, 1ul), "comparison signed/unsigned types, different values (2)");
static_assert(!cmp_less(std::numeric_limits::max(), std::numeric_limits::max()),
"comparison unsigned/signed type");
static_assert(!cmp_less(std::numeric_limits::max(), -1),
"comparison unsigned/signed type");
}
}
#endif // SAFEOPERATIONS_H