#pragma once #if defined(UTIL_ASSERT_FORMAT) #include "print.hpp" #endif #ifdef assert #pragma push_macro("assert") #undef assert #define UTIL_ASSERT_UNDEF #endif #include #include "move.hpp" #include "source_location.hpp" #include "type_traits.hpp" namespace util { template struct is_convertible : std::integral_constant< bool, (decltype(detail::test_returnable(0))::value && decltype(detail::test_implicitly_convertible(0))::value) || (is_void::value && is_void::value)> {}; template inline constexpr bool is_convertible_v = is_convertible::value; template concept boolean_testable_impl = is_convertible_v && requires { static_cast(declval()); }; template concept boolean_testable = requires(T &&t) { { !static_cast(t) } -> boolean_testable_impl; }; template concept weakly_equality_comparable_with = requires (const remove_reference_t& a, const remove_reference_t& b) { {a == b} -> boolean_testable; {a != b} -> boolean_testable; {b == a} -> boolean_testable; {b == a} -> boolean_testable; }; template concept equality_comparable = weakly_equality_comparable_with; template concept equality_comparable_with = weakly_equality_comparable_with; template inline constexpr auto assert(B&& b, const source_location& source = source_location::current()) ->void { if (!b) [[unlikely]] { #if defined (UTIL_ASSERT_FORMAT) if constexpr (is_formattable_v) { print(std::format("Assertion failed: {}\n", b)); print(std::format("In function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column())); } else { print(std::format("Assertion failed in function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column())); } #else printf("Assertion failed in function %s at %s:%d,%d\n", source.function_name(), source.file_name(), source.line(), source.column()); #endif } } template requires (equality_comparable_with) inline constexpr auto assert_eq(A&& rhs, B&& lhs, const source_location& source = source_location::current()) ->void { if (rhs != lhs) [[unlikely]] { #if defined (UTIL_ASSERT_FORMAT) if constexpr (is_formattable_v && is_formattable_v) { print(std::format("Assertion failed: {} != {}\n", rhs, lhs)); print(std::format("In function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column())); } else { print(std::format("Assertion failed in function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column())); } #else printf("Assertion failed in function %s at %s:%d,%d\n", source.function_name(), source.file_name(), source.line(), source.column()); #endif } } template inline constexpr auto assert_eq(T&& rhs, T&& lhs, const source_location& source = source_location::current()) ->void { assert_eq(FWD(rhs), FWD(lhs),source); } template requires (equality_comparable_with) inline constexpr auto assert_ne(A&& rhs, B&& lhs, const source_location& source = source_location::current()) ->void { if (rhs == lhs) [[unlikely]] { #if defined (UTIL_ASSERT_FORMAT) if constexpr (is_formattable_v && is_formattable_v) { print(std::format("Assertion failed: {} == {}\n", rhs, lhs)); print(std::format("In function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column())); } else { print(std::format("Assertion failed in function {} at {}:{},{}\n", source.function_name(), source.file_name(), source.line(), source.column())); } #else printf("Assertion failed in function %s at %s:%d,%d\n", source.function_name(), source.file_name(), source.line(), source.column()); #endif } } template inline constexpr auto assert_ne(T&& rhs, T&& lhs, const source_location& source = source_location::current()) ->void { assert_ne(FWD(rhs), FWD(lhs), source); } } // namespace util #ifdef UTIL_ASSERT_UNDEF #pragma pop_macro("assert") #undef UTIL_ASSERT_UNDEF #endif