/* 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