cpp-utils/include/assert.hpp
2022-06-26 20:48:28 +01:00

126 lines
4.6 KiB
C++

#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 <stdio.h>
#include "move.hpp"
#include "source_location.hpp"
#include "type_traits.hpp"
namespace util {
template <class From, class To>
struct is_convertible
: std::integral_constant<
bool,
(decltype(detail::test_returnable<To>(0))::value &&
decltype(detail::test_implicitly_convertible<From, To>(0))::value) ||
(is_void<From>::value && is_void<To>::value)> {};
template <typename From, typename To>
inline constexpr bool is_convertible_v = is_convertible<From, To>::value;
template <typename From>
concept boolean_testable_impl = is_convertible_v<From, bool> && requires {
static_cast<bool>(declval<From>());
};
template <typename T>
concept boolean_testable = requires(T &&t) {
{ !static_cast<T &&>(t) } -> boolean_testable_impl;
};
template <typename A, typename B>
concept weakly_equality_comparable_with = requires (const remove_reference_t<A>& a, const remove_reference_t<B>& b) {
{a == b} -> boolean_testable;
{a != b} -> boolean_testable;
{b == a} -> boolean_testable;
{b == a} -> boolean_testable;
};
template <typename T>
concept equality_comparable = weakly_equality_comparable_with<T, T>;
template <typename A, typename B>
concept equality_comparable_with = weakly_equality_comparable_with<A, B>;
template <boolean_testable B>
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<B>) {
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 <typename A, typename B>
requires (equality_comparable_with<A, B>)
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<A> && is_formattable_v<B>) {
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 <equality_comparable T>
inline constexpr auto assert_eq(T&& rhs, T&& lhs, const source_location& source = source_location::current()) ->void {
assert_eq<T,T>(FWD(rhs), FWD(lhs),source);
}
template <typename A, typename B>
requires (equality_comparable_with<A, B>)
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<A> && is_formattable_v<B>) {
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 <equality_comparable T>
inline constexpr auto assert_ne(T&& rhs, T&& lhs, const source_location& source = source_location::current()) ->void {
assert_ne<T, T>(FWD(rhs), FWD(lhs), source);
}
} // namespace util
#ifdef UTIL_ASSERT_UNDEF
#pragma pop_macro("assert")
#undef UTIL_ASSERT_UNDEF
#endif