#ifndef BASE_CHECK_OP_H_
#define BASE_CHECK_OP_H_
#include <cstddef>
#include <string>
#include <string_view>
#include <type_traits>
#include "base/base_export.h"
#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/dcheck_is_on.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/types/is_arc_pointer.h"
#include "base/types/supports_ostream_operator.h"
#include "base/types/supports_to_string.h"
namespace base {
template <class Char>
class basic_cstring_view;
}
namespace logging {
BASE_EXPORT char* CheckOpValueStr(int v);
BASE_EXPORT char* CheckOpValueStr(unsigned v);
BASE_EXPORT char* CheckOpValueStr(long v);
BASE_EXPORT char* CheckOpValueStr(unsigned long v);
BASE_EXPORT char* CheckOpValueStr(long long v);
BASE_EXPORT char* CheckOpValueStr(unsigned long long v);
BASE_EXPORT char* CheckOpValueStr(const void* v);
BASE_EXPORT char* CheckOpValueStr(std::nullptr_t v);
BASE_EXPORT char* CheckOpValueStr(double v);
BASE_EXPORT char* CheckOpValueStr(const std::string& v);
BASE_EXPORT char* CheckOpValueStr(std::string_view v);
BASE_EXPORT char* CheckOpValueStr(base::basic_cstring_view<char> v);
BASE_EXPORT char* StreamValToStr(const void* v,
void (*stream_func)(std::ostream&,
const void*));
#ifdef __has_builtin
#define SUPPORTS_BUILTIN_ADDRESSOF (__has_builtin(__builtin_addressof))
#else
#define SUPPORTS_BUILTIN_ADDRESSOF 0
#endif
template <typename T>
requires(base::internal::SupportsOstreamOperator<const T&> &&
!std::is_function_v<T> && !std::is_pointer_v<T>)
inline char* CheckOpValueStr(const T& v) {
auto f = [](std::ostream& s, const void* p) {
s << *reinterpret_cast<const T*>(p);
};
#if SUPPORTS_BUILTIN_ADDRESSOF
const void* vp = const_cast<const void*>(
reinterpret_cast<const volatile void*>(__builtin_addressof(v)));
#else
const void* vp = reinterpret_cast<const void*>(
const_cast<const char*>(&reinterpret_cast<const volatile char&>(v)));
#endif
return StreamValToStr(vp, f);
}
#undef SUPPORTS_BUILTIN_ADDRESSOF
template <typename T>
requires(std::is_pointer_v<T> &&
!std::is_function_v<std::remove_pointer_t<T>>)
inline char* CheckOpValueStr(const T& v) {
#if defined(__OBJC__)
const void* vp;
if constexpr (base::IsArcPointer<T>) {
vp = const_cast<const void*>((__bridge const volatile void*)(v));
} else {
vp = const_cast<const void*>(reinterpret_cast<const volatile void*>(v));
}
#else
const void* vp =
const_cast<const void*>(reinterpret_cast<const volatile void*>(v));
#endif
return CheckOpValueStr(vp);
}
template <typename T>
requires(!base::internal::SupportsOstreamOperator<const T&> &&
base::internal::SupportsToString<const T&>)
inline char* CheckOpValueStr(const T& v) {
return CheckOpValueStr(v.ToString());
}
template <typename T>
requires(std::is_function_v<std::remove_pointer_t<T>>)
inline char* CheckOpValueStr(const T& v) {
return CheckOpValueStr(reinterpret_cast<const void*>(v));
}
template <typename T>
requires(!base::internal::SupportsOstreamOperator<const T&> &&
std::is_enum_v<T>)
inline char* CheckOpValueStr(const T& v) {
return CheckOpValueStr(static_cast<std::underlying_type_t<T>>(v));
}
BASE_EXPORT char* CreateCheckOpLogMessageString(const char* expr_str,
char* v1_str,
char* v2_str);
#define CHECK_OP_FUNCTION_IMPL(check_failure_type, check_log_message_function, \
name, op, val1, val2, ...) \
switch (0) \
case 0: \
default: \
if (::logging::LogMessage *const message_on_fail = \
::logging::Check##name##Impl( \
(val1), (val2), \
[](char* str1, char* str2) { \
return check_log_message_function( \
::logging::CreateCheckOpLogMessageString( \
#val1 " " #op " " #val2, str1, str2) __VA_OPT__(, ) \
__VA_ARGS__); \
}); \
!message_on_fail) \
; \
else \
(check_failure_type)(message_on_fail)
#if !CHECK_WILL_STREAM()
#define CHECK_OP_INTERNAL_IMPL(name, op, val1, val2) CHECK((val1)op(val2))
#else
#define CHECK_OP_INTERNAL_IMPL(name, op, val1, val2) \
CHECK_OP_FUNCTION_IMPL(::logging::CheckNoreturnError, \
::logging::CheckNoreturnError::CheckOp, name, op, \
val1, val2)
#endif
#define CHECK_OP(name, op, val1, val2, ...) \
BASE_IF(BASE_IS_EMPTY(__VA_ARGS__), \
CHECK_OP_INTERNAL_IMPL(name, op, val1, val2), \
CHECK_OP_FUNCTION_IMPL(::logging::CheckError, \
::logging::CheckError::CheckOp, name, op, \
val1, val2, __VA_ARGS__))
#define DEFINE_CHECK_OP_IMPL(name, op) \
template <typename T, typename U, typename F> \
requires(!std::is_fundamental_v<T> || !std::is_fundamental_v<U>) \
constexpr ::logging::LogMessage* Check##name##Impl(const T& v1, const U& v2, \
F on_failure) { \
if (ANALYZER_ASSUME_TRUE(v1 op v2)) [[likely]] \
return nullptr; \
return on_failure(CheckOpValueStr(v1), CheckOpValueStr(v2)); \
} \
template <typename T, typename U, typename F> \
requires(std::is_fundamental_v<T> && std::is_fundamental_v<U>) \
constexpr ::logging::LogMessage* Check##name##Impl(T v1, U v2, \
F on_failure) { \
if (ANALYZER_ASSUME_TRUE(v1 op v2)) [[likely]] \
return nullptr; \
return on_failure(CheckOpValueStr(v1), CheckOpValueStr(v2)); \
}
DEFINE_CHECK_OP_IMPL(EQ, ==)
DEFINE_CHECK_OP_IMPL(NE, !=)
DEFINE_CHECK_OP_IMPL(LE, <=)
DEFINE_CHECK_OP_IMPL(LT, < )
DEFINE_CHECK_OP_IMPL(GE, >=)
DEFINE_CHECK_OP_IMPL(GT, > )
#undef DEFINE_CHECK_OP_IMPL
#define CHECK_EQ(val1, val2, ...) \
CHECK_OP(EQ, ==, val1, val2 __VA_OPT__(, ) __VA_ARGS__)
#define CHECK_NE(val1, val2, ...) \
CHECK_OP(NE, !=, val1, val2 __VA_OPT__(, ) __VA_ARGS__)
#define CHECK_LE(val1, val2, ...) \
CHECK_OP(LE, <=, val1, val2 __VA_OPT__(, ) __VA_ARGS__)
#define CHECK_LT(val1, val2, ...) \
CHECK_OP(LT, < , val1, val2 __VA_OPT__(, ) __VA_ARGS__)
#define CHECK_GE(val1, val2, ...) \
CHECK_OP(GE, >=, val1, val2 __VA_OPT__(, ) __VA_ARGS__)
#define CHECK_GT(val1, val2, ...) \
CHECK_OP(GT, > , val1, val2 __VA_OPT__(, ) __VA_ARGS__)
#if DCHECK_IS_ON()
#define DCHECK_OP(name, op, val1, val2) \
CHECK_OP_FUNCTION_IMPL(::logging::CheckError, \
::logging::CheckError::DCheckOp, name, op, val1, \
val2)
#else
#define DCHECK_OP(name, op, val1, val2) \
EAT_CHECK_STREAM_PARAMS((::logging::CheckOpValueStr(val1), \
::logging::CheckOpValueStr(val2), (val1)op(val2)))
#endif
#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2)
#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2)
#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2)
#define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2)
#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2)
#define DUMP_WILL_BE_CHECK_OP(name, op, val1, val2) \
CHECK_OP_FUNCTION_IMPL(::logging::CheckError, \
::logging::CheckError::DumpWillBeCheckOp, name, op, \
val1, val2)
#define DUMP_WILL_BE_CHECK_EQ(val1, val2) \
DUMP_WILL_BE_CHECK_OP(EQ, ==, val1, val2)
#define DUMP_WILL_BE_CHECK_NE(val1, val2) \
DUMP_WILL_BE_CHECK_OP(NE, !=, val1, val2)
#define DUMP_WILL_BE_CHECK_LE(val1, val2) \
DUMP_WILL_BE_CHECK_OP(LE, <=, val1, val2)
#define DUMP_WILL_BE_CHECK_LT(val1, val2) \
DUMP_WILL_BE_CHECK_OP(LT, <, val1, val2)
#define DUMP_WILL_BE_CHECK_GE(val1, val2) \
DUMP_WILL_BE_CHECK_OP(GE, >=, val1, val2)
#define DUMP_WILL_BE_CHECK_GT(val1, val2) \
DUMP_WILL_BE_CHECK_OP(GT, >, val1, val2)
}
#endif