From 07a6a42d865d24de13e4a65ff5f52abf12aa3972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Sat, 24 Dec 2016 17:46:41 +0100 Subject: [PATCH] Differentiate between precondition checks and internal assertions --- CMakeLists.txt | 16 +++++++++++++++- include/type_safe/arithmetic_policy.hpp | 14 ++++++++------ include/type_safe/compact_optional.hpp | 2 +- include/type_safe/config.hpp | 6 +++++- include/type_safe/constrained_type.hpp | 5 +++-- include/type_safe/deferred_construction.hpp | 10 +++++----- include/type_safe/detail/assert.hpp | 6 ++++++ include/type_safe/flag.hpp | 2 +- include/type_safe/index.hpp | 2 +- include/type_safe/integer.hpp | 14 +++++++------- include/type_safe/narrow_cast.hpp | 3 ++- include/type_safe/optional.hpp | 8 ++++---- include/type_safe/tagged_union.hpp | 2 +- include/type_safe/variant.hpp | 6 +++--- include/type_safe/visitor.hpp | 4 ++-- 15 files changed, 64 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 70e26edf..e8125518 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,13 +8,26 @@ project(TYPE_SAFE) include(external/external.cmake) # options -option(TYPE_SAFE_ENABLE_ASSERTIONS "whether or not to enable assertions for the type_safe library" ON) +if(CMAKE_BUILD_TYPE MATCHES Debug) + set(_type_safe_default_assertions ON) +else() + set(_type_safe_default_assertions OFF) +endif() + +option(TYPE_SAFE_ENABLE_ASSERTIONS "whether or not to enable internal assertions for the type_safe library" ${_type_safe_default_assertions}) if(${TYPE_SAFE_ENABLE_ASSERTIONS}) set(_type_safe_enable_assertions 1) else() set(_type_safe_enable_assertions 0) endif() +option(TYPE_SAFE_ENABLE_PRECONDITION_CHECKS "whether or not to enable precondition checks" ON) +if(${TYPE_SAFE_ENABLE_PRECONDITION_CHECKS}) + set(_type_safe_enable_precondition_checks 1) +else() + set(_type_safe_enable_precondition_checks 0) +endif() + option(TYPE_SAFE_ENABLE_WRAPPER "whether or not the wrappers in types.hpp are used" ON) if(${TYPE_SAFE_ENABLE_WRAPPER}) set(_type_safe_enable_wrapper 1) @@ -66,6 +79,7 @@ target_sources(type_safe INTERFACE ${detail_header_files} ${header_files}) target_include_directories(type_safe INTERFACE include/) target_compile_definitions(type_safe INTERFACE TYPE_SAFE_ENABLE_ASSERTIONS=${_type_safe_enable_assertions} + TYPE_SAFE_ENABLE_PRECONDITION_CHECKS=${_type_safe_enable_precondition_checks} TYPE_SAFE_ENABLE_WRAPPER=${_type_safe_enable_wrapper} TYPE_SAFE_ARITHMETIC_UB=${_type_safe_arithmetic_ub}) target_link_libraries(type_safe INTERFACE debug_assert) diff --git a/include/type_safe/arithmetic_policy.hpp b/include/type_safe/arithmetic_policy.hpp index ef452dde..89cb0777 100644 --- a/include/type_safe/arithmetic_policy.hpp +++ b/include/type_safe/arithmetic_policy.hpp @@ -138,7 +138,7 @@ namespace type_safe TYPE_SAFE_FORCE_INLINE static constexpr T do_addition(const T& a, const T& b) noexcept { return detail::will_addition_error(detail::arithmetic_tag_for{}, a, b) ? - (DEBUG_UNREACHABLE(detail::assert_handler{}, + (DEBUG_UNREACHABLE(detail::precondition_error_handler{}, "addition will result in overflow"), a) : a + b; @@ -148,7 +148,7 @@ namespace type_safe TYPE_SAFE_FORCE_INLINE static constexpr T do_subtraction(const T& a, const T& b) noexcept { return detail::will_subtraction_error(detail::arithmetic_tag_for{}, a, b) ? - (DEBUG_UNREACHABLE(detail::assert_handler{}, + (DEBUG_UNREACHABLE(detail::precondition_error_handler{}, "subtraction will result in underflow"), a) : a - b; @@ -158,7 +158,7 @@ namespace type_safe TYPE_SAFE_FORCE_INLINE static constexpr T do_multiplication(const T& a, const T& b) noexcept { return detail::will_multiplication_error(detail::arithmetic_tag_for{}, a, b) ? - (DEBUG_UNREACHABLE(detail::assert_handler{}, + (DEBUG_UNREACHABLE(detail::precondition_error_handler{}, "multiplication will result in overflow"), a) : a * b; @@ -168,7 +168,8 @@ namespace type_safe TYPE_SAFE_FORCE_INLINE static constexpr T do_division(const T& a, const T& b) noexcept { return detail::will_division_error(detail::arithmetic_tag_for{}, a, b) ? - (DEBUG_UNREACHABLE(detail::assert_handler{}, "division by zero/overflow"), + (DEBUG_UNREACHABLE(detail::precondition_error_handler{}, + "division by zero/overflow"), a) : a / b; } @@ -177,7 +178,8 @@ namespace type_safe TYPE_SAFE_FORCE_INLINE static constexpr T do_modulo(const T& a, const T& b) noexcept { return detail::will_modulo_error(detail::arithmetic_tag_for{}, a, b) ? - (DEBUG_UNREACHABLE(detail::assert_handler{}, "modulo by zero"), a) : + (DEBUG_UNREACHABLE(detail::precondition_error_handler{}, "modulo by zero"), + a) : a % b; } }; @@ -195,7 +197,7 @@ namespace type_safe error(const char* msg) : std::range_error(msg) { #if !TYPE_SAFE_USE_EXCEPTIONS - DEBUG_UNREACHABLE(detail::assert_handler{}, msg); + DEBUG_UNREACHABLE(detail::precondition_error_handler{}, msg); #endif } }; diff --git a/include/type_safe/compact_optional.hpp b/include/type_safe/compact_optional.hpp index f35b7cfe..7a3c2af1 100644 --- a/include/type_safe/compact_optional.hpp +++ b/include/type_safe/compact_optional.hpp @@ -78,7 +78,7 @@ namespace type_safe typename std::enable_if::value>::type { storage_ = static_cast(value_type(std::forward(args)...)); - DEBUG_ASSERT(has_value(), detail::assert_handler{}, + DEBUG_ASSERT(has_value(), detail::precondition_error_handler{}, "create_value() called creating an invalid value"); } diff --git a/include/type_safe/config.hpp b/include/type_safe/config.hpp index 9c842887..54d81260 100644 --- a/include/type_safe/config.hpp +++ b/include/type_safe/config.hpp @@ -9,7 +9,11 @@ #include #ifndef TYPE_SAFE_ENABLE_ASSERTIONS -#define TYPE_SAFE_ENABLE_ASSERTIONS 1 +#define TYPE_SAFE_ENABLE_ASSERTIONS 0 +#endif + +#ifndef TYPE_SAFE_ENABLE_PRECONDITION_CHECKS +#define TYPE_SAFE_ENABLE_PRECONDITION_CHECKS 1 #endif #ifndef TYPE_SAFE_ENABLE_WRAPPER diff --git a/include/type_safe/constrained_type.hpp b/include/type_safe/constrained_type.hpp index 5227d5a4..41deef41 100644 --- a/include/type_safe/constrained_type.hpp +++ b/include/type_safe/constrained_type.hpp @@ -21,7 +21,8 @@ namespace type_safe template static void verify(const Value& val, const Predicate& p) { - DEBUG_ASSERT(p(val), detail::assert_handler{}, "value does not fulfill constraint"); + DEBUG_ASSERT(p(val), detail::precondition_error_handler{}, + "value does not fulfill constraint"); } }; @@ -185,7 +186,7 @@ namespace type_safe /// \requires It must not be in the moved-from state. value_type& get() noexcept { - DEBUG_ASSERT(value_, detail::assert_handler{}); + DEBUG_ASSERT(value_, detail::precondition_error_handler{}); return value_->value_; } diff --git a/include/type_safe/deferred_construction.hpp b/include/type_safe/deferred_construction.hpp index 057f63ed..c1acbe88 100644 --- a/include/type_safe/deferred_construction.hpp +++ b/include/type_safe/deferred_construction.hpp @@ -107,7 +107,7 @@ namespace type_safe template void emplace(Args&&... args) { - DEBUG_ASSERT(!has_value(), detail::assert_handler{}); + DEBUG_ASSERT(!has_value(), detail::precondition_error_handler{}); ::new (as_void()) value_type(std::forward(args)...); initialized_ = true; } @@ -129,7 +129,7 @@ namespace type_safe /// \requires `has_value() == true`. value_type& value() TYPE_SAFE_LVALUE_REF noexcept { - DEBUG_ASSERT(has_value(), detail::assert_handler{}); + DEBUG_ASSERT(has_value(), detail::precondition_error_handler{}); return *static_cast(as_void()); } @@ -137,7 +137,7 @@ namespace type_safe /// \requires `has_value() == true`. const value_type& value() const TYPE_SAFE_LVALUE_REF noexcept { - DEBUG_ASSERT(has_value(), detail::assert_handler{}); + DEBUG_ASSERT(has_value(), detail::precondition_error_handler{}); return *static_cast(as_void()); } @@ -146,7 +146,7 @@ namespace type_safe /// \requires `has_value() == true`. value_type&& value() && noexcept { - DEBUG_ASSERT(has_value(), detail::assert_handler{}); + DEBUG_ASSERT(has_value(), detail::precondition_error_handler{}); return std::move(*static_cast(as_void())); } @@ -154,7 +154,7 @@ namespace type_safe /// \requires `has_value() == true`. const value_type&& value() const && noexcept { - DEBUG_ASSERT(has_value(), detail::assert_handler{}); + DEBUG_ASSERT(has_value(), detail::precondition_error_handler{}); return std::move(*static_cast(as_void())); } #endif diff --git a/include/type_safe/detail/assert.hpp b/include/type_safe/detail/assert.hpp index 6244decc..c9fec6cc 100644 --- a/include/type_safe/detail/assert.hpp +++ b/include/type_safe/detail/assert.hpp @@ -17,6 +17,12 @@ namespace type_safe debug_assert::default_handler { }; + + struct precondition_error_handler + : debug_assert::set_level, + debug_assert::default_handler + { + }; } // namespace detail } // namespace type_safe diff --git a/include/type_safe/flag.hpp b/include/type_safe/flag.hpp index 096a41d5..637ef865 100644 --- a/include/type_safe/flag.hpp +++ b/include/type_safe/flag.hpp @@ -78,7 +78,7 @@ namespace type_safe template > void change(T new_state) noexcept { - DEBUG_ASSERT(state_ != new_state, detail::assert_handler{}); + DEBUG_ASSERT(state_ != new_state, detail::precondition_error_handler{}); state_ = new_state; } diff --git a/include/type_safe/index.hpp b/include/type_safe/index.hpp index d9ff940a..750a7006 100644 --- a/include/type_safe/index.hpp +++ b/include/type_safe/index.hpp @@ -174,7 +174,7 @@ namespace type_safe { DEBUG_ASSERT(detail::index_valid(detail::member_size{}, obj, static_cast(get(index))), - detail::assert_handler{}); + detail::precondition_error_handler{}); return std::forward(obj)[static_cast(get(index))]; } diff --git a/include/type_safe/integer.hpp b/include/type_safe/integer.hpp index d1529d7a..439177f8 100644 --- a/include/type_safe/integer.hpp +++ b/include/type_safe/integer.hpp @@ -352,9 +352,9 @@ namespace type_safe using result_type = make_signed_t; return i <= Integer(std::numeric_limits::max()) ? static_cast(i) : - (DEBUG_UNREACHABLE(detail::assert_handler{}, "conversion " - "would " - "overflow"), + (DEBUG_UNREACHABLE(detail::precondition_error_handler{}, "conversion " + "would " + "overflow"), result_type()); } @@ -383,10 +383,10 @@ namespace type_safe TYPE_SAFE_FORCE_INLINE constexpr make_unsigned_t make_unsigned(const Integer& i) { using result_type = make_unsigned_t; - return i >= Integer(0) ? - static_cast(i) : - (DEBUG_UNREACHABLE(detail::assert_handler{}, "conversion would underflow"), - result_type(0)); + return i >= Integer(0) ? static_cast(i) : + (DEBUG_UNREACHABLE(detail::precondition_error_handler{}, + "conversion would underflow"), + result_type(0)); } /// \returns A new [ts::integer]() of the corresponding unsigned integer type. diff --git a/include/type_safe/narrow_cast.hpp b/include/type_safe/narrow_cast.hpp index 0c5aad86..7ca12da0 100644 --- a/include/type_safe/narrow_cast.hpp +++ b/include/type_safe/narrow_cast.hpp @@ -72,7 +72,8 @@ namespace type_safe TYPE_SAFE_FORCE_INLINE constexpr Target narrow_cast(const Source& source) noexcept { return detail::is_narrowing(source) ? - (DEBUG_UNREACHABLE(detail::assert_handler{}, "conversion would truncate value"), + (DEBUG_UNREACHABLE(detail::precondition_error_handler{}, + "conversion would truncate value"), Target()) : static_cast(source); } diff --git a/include/type_safe/optional.hpp b/include/type_safe/optional.hpp index 7cbc3cda..88c90b51 100644 --- a/include/type_safe/optional.hpp +++ b/include/type_safe/optional.hpp @@ -375,7 +375,7 @@ namespace type_safe /// \requires `has_value() == true`. auto value() TYPE_SAFE_LVALUE_REF noexcept -> decltype(std::declval().get_value()) { - DEBUG_ASSERT(has_value(), detail::assert_handler{}); + DEBUG_ASSERT(has_value(), detail::precondition_error_handler{}); return get_storage().get_value(); } @@ -384,7 +384,7 @@ namespace type_safe auto value() const TYPE_SAFE_LVALUE_REF noexcept -> decltype(std::declval().get_value()) { - DEBUG_ASSERT(has_value(), detail::assert_handler{}); + DEBUG_ASSERT(has_value(), detail::precondition_error_handler{}); return get_storage().get_value(); } @@ -393,7 +393,7 @@ namespace type_safe /// \requires `has_value() == true`. auto value() && noexcept -> decltype(std::declval().get_value()) { - DEBUG_ASSERT(has_value(), detail::assert_handler{}); + DEBUG_ASSERT(has_value(), detail::precondition_error_handler{}); return std::move(get_storage()).get_value(); } @@ -401,7 +401,7 @@ namespace type_safe /// \requires `has_value() == true`. auto value() const && noexcept -> decltype(std::declval().get_value()) { - DEBUG_ASSERT(has_value(), detail::assert_handler{}); + DEBUG_ASSERT(has_value(), detail::precondition_error_handler{}); return std::move(get_storage()).get_value(); } #endif diff --git a/include/type_safe/tagged_union.hpp b/include/type_safe/tagged_union.hpp index 5206f693..74692e4a 100644 --- a/include/type_safe/tagged_union.hpp +++ b/include/type_safe/tagged_union.hpp @@ -256,7 +256,7 @@ namespace type_safe template void check(union_type type) const noexcept { - DEBUG_ASSERT(cur_type_ == type, detail::assert_handler{}, + DEBUG_ASSERT(cur_type_ == type, detail::precondition_error_handler{}, "different type stored in union"); } diff --git a/include/type_safe/variant.hpp b/include/type_safe/variant.hpp index ca254dda..29c41f65 100644 --- a/include/type_safe/variant.hpp +++ b/include/type_safe/variant.hpp @@ -142,7 +142,7 @@ namespace type_safe /// \group ctor_union explicit basic_variant(const tagged_union& u) { - DEBUG_ASSERT(allow_empty::value || u.has_value(), detail::assert_handler{}); + DEBUG_ASSERT(allow_empty::value || u.has_value(), detail::precondition_error_handler{}); if (u.has_value()) copy(storage_.get_union(), u); } @@ -150,7 +150,7 @@ namespace type_safe /// \group ctor_union explicit basic_variant(tagged_union&& u) { - DEBUG_ASSERT(allow_empty::value || u.has_value(), detail::assert_handler{}); + DEBUG_ASSERT(allow_empty::value || u.has_value(), detail::precondition_error_handler{}); if (u.has_value()) move(storage_.get_union(), std::move(u)); } @@ -358,7 +358,7 @@ namespace type_safe /// \requires The variant must be empty. nullvar_t value(variant_type) const noexcept { - DEBUG_ASSERT(!has_value(), detail::assert_handler{}); + DEBUG_ASSERT(!has_value(), detail::precondition_error_handler{}); return nullvar; } diff --git a/include/type_safe/visitor.hpp b/include/type_safe/visitor.hpp index 3325415f..9a4282e2 100644 --- a/include/type_safe/visitor.hpp +++ b/include/type_safe/visitor.hpp @@ -207,7 +207,7 @@ namespace type_safe std::forward(visitor), std::forward(args)..., nullvar)) { DEBUG_ASSERT(std::decay::type::allow_empty::value && !variant.has_value(), - assert_handler{}, "variant in invalid state for visitor"); + precondition_error_handler{}, "variant in invalid state for visitor"); return visit_variant_impl::call(std::forward( visitor), std::forward( @@ -265,7 +265,7 @@ namespace type_safe std::forward(args)..., nullvar)) { DEBUG_ASSERT(std::decay::type::allow_empty::value && !variant.has_value(), - assert_handler{}, "variant in invalid state for visitor"); + precondition_error_handler{}, "variant in invalid state for visitor"); return visit_variant_impl::call(std::forward(visitor), std::forward(rest)...,