Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 1 | // Copyright 2020 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #ifndef BASE_CHECK_OP_H_ |
| 6 | #define BASE_CHECK_OP_H_ |
| 7 | |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 8 | #include <cstddef> |
| 9 | #include <type_traits> |
| 10 | |
| 11 | #include "base/check.h" |
| 12 | #include "base/template_util.h" |
| 13 | |
| 14 | // This header defines the (DP)CHECK_EQ etc. macros. |
| 15 | // |
| 16 | // (DP)CHECK_EQ(x, y) is similar to (DP)CHECK(x == y) but will also log the |
Hans Wennborg | d80f6e6 | 2020-04-27 19:21:56 | [diff] [blame] | 17 | // values of x and y if the condition doesn't hold. This works for basic types |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 18 | // and types with an operator<< or .ToString() method. |
| 19 | // |
| 20 | // The operands are evaluated exactly once, and even in build modes where e.g. |
| 21 | // DCHECK is disabled, the operands and their stringification methods are still |
| 22 | // referenced to avoid warnings about unused variables or functions. |
| 23 | // |
| 24 | // To support the stringification of the check operands, this header is |
| 25 | // *significantly* larger than base/check.h, so it should be avoided in common |
| 26 | // headers. |
Hans Wennborg | d80f6e6 | 2020-04-27 19:21:56 | [diff] [blame] | 27 | // |
| 28 | // This header also provides the (DP)CHECK macros (by including check.h), so if |
| 29 | // you use e.g. both CHECK_EQ and CHECK, including this header is enough. If you |
| 30 | // only use CHECK however, please include the smaller check.h instead. |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 31 | |
| 32 | namespace logging { |
| 33 | |
| 34 | // Functions for turning check operand values into strings. |
| 35 | // Caller takes ownership of the returned string. |
| 36 | BASE_EXPORT char* CheckOpValueStr(int v); |
| 37 | BASE_EXPORT char* CheckOpValueStr(unsigned v); |
Hans Wennborg | 338a7f40 | 2020-04-20 19:52:32 | [diff] [blame] | 38 | BASE_EXPORT char* CheckOpValueStr(long v); |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 39 | BASE_EXPORT char* CheckOpValueStr(unsigned long v); |
Hans Wennborg | 338a7f40 | 2020-04-20 19:52:32 | [diff] [blame] | 40 | BASE_EXPORT char* CheckOpValueStr(long long v); |
| 41 | BASE_EXPORT char* CheckOpValueStr(unsigned long long v); |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 42 | BASE_EXPORT char* CheckOpValueStr(const void* v); |
| 43 | BASE_EXPORT char* CheckOpValueStr(std::nullptr_t v); |
| 44 | BASE_EXPORT char* CheckOpValueStr(double v); |
| 45 | |
| 46 | // Convert a streamable value to string out-of-line to avoid <sstream>. |
| 47 | BASE_EXPORT char* StreamValToStr(const void* v, |
| 48 | void (*stream_func)(std::ostream&, |
| 49 | const void*)); |
| 50 | |
Stephan Hartmann | 9d518a6 | 2020-06-01 19:45:01 | [diff] [blame] | 51 | #ifdef __has_builtin |
| 52 | #define SUPPORTS_BUILTIN_ADDRESSOF (__has_builtin(__builtin_addressof)) |
| 53 | #else |
| 54 | #define SUPPORTS_BUILTIN_ADDRESSOF 0 |
Stephan Hartmann | c65d17e | 2020-05-04 16:00:28 | [diff] [blame] | 55 | #endif |
| 56 | |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 57 | template <typename T> |
| 58 | inline typename std::enable_if< |
| 59 | base::internal::SupportsOstreamOperator<const T&>::value && |
| 60 | !std::is_function<typename std::remove_pointer<T>::type>::value, |
| 61 | char*>::type |
| 62 | CheckOpValueStr(const T& v) { |
| 63 | auto f = [](std::ostream& s, const void* p) { |
| 64 | s << *reinterpret_cast<const T*>(p); |
| 65 | }; |
| 66 | |
| 67 | // operator& might be overloaded, so do the std::addressof dance. |
| 68 | // __builtin_addressof is preferred since it also handles Obj-C ARC pointers. |
| 69 | // Some casting is still needed, because T might be volatile. |
Stephan Hartmann | 9d518a6 | 2020-06-01 19:45:01 | [diff] [blame] | 70 | #if SUPPORTS_BUILTIN_ADDRESSOF |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 71 | const void* vp = const_cast<const void*>( |
| 72 | reinterpret_cast<const volatile void*>(__builtin_addressof(v))); |
| 73 | #else |
| 74 | const void* vp = reinterpret_cast<const void*>( |
| 75 | const_cast<const char*>(&reinterpret_cast<const volatile char&>(v))); |
| 76 | #endif |
| 77 | return StreamValToStr(vp, f); |
| 78 | } |
| 79 | |
Stephan Hartmann | 9d518a6 | 2020-06-01 19:45:01 | [diff] [blame] | 80 | #undef SUPPORTS_BUILTIN_ADDRESSOF |
| 81 | |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 82 | // Overload for types that have no operator<< but do have .ToString() defined. |
| 83 | template <typename T> |
| 84 | inline typename std::enable_if< |
| 85 | !base::internal::SupportsOstreamOperator<const T&>::value && |
| 86 | base::internal::SupportsToString<const T&>::value, |
| 87 | char*>::type |
| 88 | CheckOpValueStr(const T& v) { |
Hitoshi Yoshida | ca053b9 | 2020-04-20 19:38:06 | [diff] [blame] | 89 | // .ToString() may not return a std::string, e.g. blink::WTF::String. |
| 90 | return CheckOpValueStr(v.ToString()); |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 91 | } |
| 92 | |
| 93 | // Provide an overload for functions and function pointers. Function pointers |
| 94 | // don't implicitly convert to void* but do implicitly convert to bool, so |
| 95 | // without this function pointers are always printed as 1 or 0. (MSVC isn't |
| 96 | // standards-conforming here and converts function pointers to regular |
| 97 | // pointers, so this is a no-op for MSVC.) |
| 98 | template <typename T> |
| 99 | inline typename std::enable_if< |
| 100 | std::is_function<typename std::remove_pointer<T>::type>::value, |
| 101 | char*>::type |
| 102 | CheckOpValueStr(const T& v) { |
| 103 | return CheckOpValueStr(reinterpret_cast<const void*>(v)); |
| 104 | } |
| 105 | |
| 106 | // We need overloads for enums that don't support operator<<. |
| 107 | // (i.e. scoped enums where no operator<< overload was declared). |
| 108 | template <typename T> |
| 109 | inline typename std::enable_if< |
| 110 | !base::internal::SupportsOstreamOperator<const T&>::value && |
| 111 | std::is_enum<T>::value, |
| 112 | char*>::type |
| 113 | CheckOpValueStr(const T& v) { |
| 114 | return CheckOpValueStr( |
| 115 | static_cast<typename std::underlying_type<T>::type>(v)); |
| 116 | } |
| 117 | |
| 118 | // Captures the result of a CHECK_op and facilitates testing as a boolean. |
| 119 | class CheckOpResult { |
| 120 | public: |
| 121 | // An empty result signals success. |
Hans Wennborg | 773f5237 | 2020-05-15 19:25:32 | [diff] [blame] | 122 | constexpr CheckOpResult() {} |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 123 | |
| 124 | // A non-success result. expr_str is something like "foo != bar". v1_str and |
| 125 | // v2_str are the stringified run-time values of foo and bar. Takes ownership |
| 126 | // of v1_str and v2_str. |
| 127 | BASE_EXPORT CheckOpResult(const char* expr_str, char* v1_str, char* v2_str); |
| 128 | |
| 129 | // Returns true if the check succeeded. |
| 130 | constexpr explicit operator bool() const { return !message_; } |
| 131 | |
| 132 | friend class CheckError; |
| 133 | |
| 134 | private: |
Lei Zhang | 4a0950f | 2020-04-18 07:14:02 | [diff] [blame] | 135 | char* message_ = nullptr; |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 136 | }; |
| 137 | |
| 138 | #if defined(OFFICIAL_BUILD) && defined(NDEBUG) |
| 139 | |
| 140 | // Discard log strings to reduce code bloat. |
| 141 | #define CHECK_OP(name, op, val1, val2) CHECK((val1)op(val2)) |
| 142 | |
| 143 | #else |
| 144 | |
| 145 | // Helper macro for binary operators. |
| 146 | // The 'switch' is used to prevent the 'else' from being ambiguous when the |
| 147 | // macro is used in an 'if' clause such as: |
| 148 | // if (a == 1) |
| 149 | // CHECK_EQ(2, a); |
| 150 | #define CHECK_OP(name, op, val1, val2) \ |
| 151 | switch (0) \ |
| 152 | case 0: \ |
| 153 | default: \ |
| 154 | if (::logging::CheckOpResult true_if_passed = \ |
| 155 | ::logging::Check##name##Impl((val1), (val2), \ |
| 156 | #val1 " " #op " " #val2)) \ |
| 157 | ; \ |
| 158 | else \ |
| 159 | ::logging::CheckError::CheckOp(__FILE__, __LINE__, &true_if_passed) \ |
| 160 | .stream() |
| 161 | |
| 162 | #endif |
| 163 | |
Tom Anderson | c84fb46 | 2020-06-08 23:08:53 | [diff] [blame] | 164 | // The second overload avoids address-taking of static members for |
| 165 | // fundamental types. |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 166 | #define DEFINE_CHECK_OP_IMPL(name, op) \ |
Tom Anderson | c84fb46 | 2020-06-08 23:08:53 | [diff] [blame] | 167 | template <typename T, typename U, \ |
| 168 | std::enable_if_t<!std::is_fundamental<T>::value || \ |
| 169 | !std::is_fundamental<U>::value, \ |
| 170 | int> = 0> \ |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 171 | constexpr ::logging::CheckOpResult Check##name##Impl( \ |
| 172 | const T& v1, const U& v2, const char* expr_str) { \ |
| 173 | if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ |
| 174 | return ::logging::CheckOpResult(); \ |
Lei Zhang | 4a0950f | 2020-04-18 07:14:02 | [diff] [blame] | 175 | return ::logging::CheckOpResult(expr_str, CheckOpValueStr(v1), \ |
| 176 | CheckOpValueStr(v2)); \ |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 177 | } \ |
Tom Anderson | c84fb46 | 2020-06-08 23:08:53 | [diff] [blame] | 178 | template <typename T, typename U, \ |
| 179 | std::enable_if_t<std::is_fundamental<T>::value && \ |
| 180 | std::is_fundamental<U>::value, \ |
| 181 | int> = 0> \ |
| 182 | constexpr ::logging::CheckOpResult Check##name##Impl(T v1, U v2, \ |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 183 | const char* expr_str) { \ |
| 184 | if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ |
| 185 | return ::logging::CheckOpResult(); \ |
Lei Zhang | 4a0950f | 2020-04-18 07:14:02 | [diff] [blame] | 186 | return ::logging::CheckOpResult(expr_str, CheckOpValueStr(v1), \ |
| 187 | CheckOpValueStr(v2)); \ |
Hans Wennborg | 12aea3e | 2020-04-14 15:29:00 | [diff] [blame] | 188 | } |
| 189 | |
| 190 | // clang-format off |
| 191 | DEFINE_CHECK_OP_IMPL(EQ, ==) |
| 192 | DEFINE_CHECK_OP_IMPL(NE, !=) |
| 193 | DEFINE_CHECK_OP_IMPL(LE, <=) |
| 194 | DEFINE_CHECK_OP_IMPL(LT, < ) |
| 195 | DEFINE_CHECK_OP_IMPL(GE, >=) |
| 196 | DEFINE_CHECK_OP_IMPL(GT, > ) |
| 197 | #undef DEFINE_CHECK_OP_IMPL |
| 198 | #define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2) |
| 199 | #define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2) |
| 200 | #define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2) |
| 201 | #define CHECK_LT(val1, val2) CHECK_OP(LT, < , val1, val2) |
| 202 | #define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2) |
| 203 | #define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2) |
| 204 | // clang-format on |
| 205 | |
| 206 | #if DCHECK_IS_ON() |
| 207 | |
| 208 | #define DCHECK_OP(name, op, val1, val2) \ |
| 209 | switch (0) \ |
| 210 | case 0: \ |
| 211 | default: \ |
| 212 | if (::logging::CheckOpResult true_if_passed = \ |
| 213 | ::logging::Check##name##Impl((val1), (val2), \ |
| 214 | #val1 " " #op " " #val2)) \ |
| 215 | ; \ |
| 216 | else \ |
| 217 | ::logging::CheckError::DCheckOp(__FILE__, __LINE__, &true_if_passed) \ |
| 218 | .stream() |
| 219 | |
| 220 | #else |
| 221 | |
| 222 | // Don't do any evaluation but still reference the same stuff as when enabled. |
| 223 | #define DCHECK_OP(name, op, val1, val2) \ |
| 224 | EAT_CHECK_STREAM_PARAMS((::logging::CheckOpValueStr(val1), \ |
| 225 | ::logging::CheckOpValueStr(val2), (val1)op(val2))) |
| 226 | |
| 227 | #endif |
| 228 | |
| 229 | // clang-format off |
| 230 | #define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2) |
| 231 | #define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2) |
| 232 | #define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2) |
| 233 | #define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2) |
| 234 | #define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2) |
| 235 | #define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2) |
| 236 | // clang-format on |
| 237 | |
| 238 | } // namespace logging |
| 239 | |
| 240 | #endif // BASE_CHECK_OP_H_ |