1 // Copyright 2022 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // -----------------------------------------------------------------------------
16 // File: log/internal/check_op.h
17 // -----------------------------------------------------------------------------
18 //
19 // This file declares helpers routines and macros used to implement `CHECK`
20 // macros.
21
22 #ifndef ABSL_LOG_INTERNAL_CHECK_OP_H_
23 #define ABSL_LOG_INTERNAL_CHECK_OP_H_
24
25 #include <stdint.h>
26
27 #include <cstddef>
28 #include <ostream>
29 #include <sstream>
30 #include <string>
31 #include <type_traits>
32 #include <utility>
33
34 #include "absl/base/attributes.h"
35 #include "absl/base/config.h"
36 #include "absl/base/nullability.h"
37 #include "absl/base/optimization.h"
38 #include "absl/log/internal/nullguard.h"
39 #include "absl/log/internal/nullstream.h"
40 #include "absl/log/internal/strip.h"
41 #include "absl/strings/has_absl_stringify.h"
42 #include "absl/strings/string_view.h"
43
44 // `ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL` wraps string literals that
45 // should be stripped when `ABSL_MIN_LOG_LEVEL` exceeds `kFatal`.
46 #ifdef ABSL_MIN_LOG_LEVEL
47 #define ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(literal) \
48 (::absl::LogSeverity::kFatal >= \
49 static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) \
50 ? (literal) \
51 : "")
52 #else
53 #define ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(literal) (literal)
54 #endif
55
56 #ifdef NDEBUG
57 // `NDEBUG` is defined, so `DCHECK_EQ(x, y)` and so on do nothing. However, we
58 // still want the compiler to parse `x` and `y`, because we don't want to lose
59 // potentially useful errors and warnings.
60 #define ABSL_LOG_INTERNAL_DCHECK_NOP(x, y) \
61 while (false && ((void)(x), (void)(y), 0)) \
62 ::absl::log_internal::NullStream().InternalStream()
63 #endif
64
65 #define ABSL_LOG_INTERNAL_CHECK_OP(name, op, val1, val1_text, val2, val2_text) \
66 while (::std::string* absl_log_internal_check_op_result \
67 ABSL_LOG_INTERNAL_ATTRIBUTE_UNUSED_IF_STRIP_LOG = \
68 ::absl::log_internal::name##Impl( \
69 ::absl::log_internal::GetReferenceableValue(val1), \
70 ::absl::log_internal::GetReferenceableValue(val2), \
71 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL( \
72 val1_text " " #op " " val2_text))) \
73 ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true) \
74 ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_op_result).InternalStream()
75 #define ABSL_LOG_INTERNAL_QCHECK_OP(name, op, val1, val1_text, val2, \
76 val2_text) \
77 while (::std::string* absl_log_internal_qcheck_op_result = \
78 ::absl::log_internal::name##Impl( \
79 ::absl::log_internal::GetReferenceableValue(val1), \
80 ::absl::log_internal::GetReferenceableValue(val2), \
81 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL( \
82 val1_text " " #op " " val2_text))) \
83 ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true) \
84 ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_op_result).InternalStream()
85 #define ABSL_LOG_INTERNAL_CHECK_STROP(func, op, expected, s1, s1_text, s2, \
86 s2_text) \
87 while (::std::string* absl_log_internal_check_strop_result = \
88 ::absl::log_internal::Check##func##expected##Impl( \
89 (s1), (s2), \
90 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op \
91 " " s2_text))) \
92 ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true) \
93 ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_strop_result) \
94 .InternalStream()
95 #define ABSL_LOG_INTERNAL_QCHECK_STROP(func, op, expected, s1, s1_text, s2, \
96 s2_text) \
97 while (::std::string* absl_log_internal_qcheck_strop_result = \
98 ::absl::log_internal::Check##func##expected##Impl( \
99 (s1), (s2), \
100 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op \
101 " " s2_text))) \
102 ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true) \
103 ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_strop_result) \
104 .InternalStream()
105 // This one is tricky:
106 // * We must evaluate `val` exactly once, yet we need to do two things with it:
107 // evaluate `.ok()` and (sometimes) `.ToString()`.
108 // * `val` might be an `absl::Status` or some `absl::StatusOr<T>`.
109 // * `val` might be e.g. `ATemporary().GetStatus()`, which may return a
110 // reference to a member of `ATemporary` that is only valid until the end of
111 // the full expression.
112 // * We don't want this file to depend on `absl::Status` `#include`s or linkage,
113 // nor do we want to move the definition to status and introduce a dependency
114 // in the other direction. We can be assured that callers must already have a
115 // `Status` and the necessary `#include`s and linkage.
116 // * Callsites should be small and fast (at least when `val.ok()`): one branch,
117 // minimal stack footprint.
118 // * In particular, the string concat stuff should be out-of-line and emitted
119 // in only one TU to save linker input size
120 // * We want the `val.ok()` check inline so static analyzers and optimizers can
121 // see it.
122 // * As usual, no braces so we can stream into the expansion with `operator<<`.
123 // * Also as usual, it must expand to a single (partial) statement with no
124 // ambiguous-else problems.
125 // * When stripped by `ABSL_MIN_LOG_LEVEL`, we must discard the `<expr> is OK`
126 // string literal and abort without doing any streaming. We don't need to
127 // strip the call to stringify the non-ok `Status` as long as we don't log it;
128 // dropping the `Status`'s message text is out of scope.
129 #define ABSL_LOG_INTERNAL_CHECK_OK(val, val_text) \
130 for (::std::pair<const ::absl::Status*, ::std::string*> \
131 absl_log_internal_check_ok_goo; \
132 absl_log_internal_check_ok_goo.first = \
133 ::absl::log_internal::AsStatus(val), \
134 absl_log_internal_check_ok_goo.second = \
135 ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \
136 ? nullptr \
137 : ::absl::status_internal::MakeCheckFailString( \
138 absl_log_internal_check_ok_goo.first, \
139 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text \
140 " is OK")), \
141 !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \
142 ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true) \
143 ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_ok_goo.second) \
144 .InternalStream()
145 #define ABSL_LOG_INTERNAL_QCHECK_OK(val, val_text) \
146 for (::std::pair<const ::absl::Status*, ::std::string*> \
147 absl_log_internal_qcheck_ok_goo; \
148 absl_log_internal_qcheck_ok_goo.first = \
149 ::absl::log_internal::AsStatus(val), \
150 absl_log_internal_qcheck_ok_goo.second = \
151 ABSL_PREDICT_TRUE(absl_log_internal_qcheck_ok_goo.first->ok()) \
152 ? nullptr \
153 : ::absl::status_internal::MakeCheckFailString( \
154 absl_log_internal_qcheck_ok_goo.first, \
155 ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text \
156 " is OK")), \
157 !ABSL_PREDICT_TRUE(absl_log_internal_qcheck_ok_goo.first->ok());) \
158 ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true) \
159 ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_ok_goo.second) \
160 .InternalStream()
161
162 namespace absl {
163 ABSL_NAMESPACE_BEGIN
164
165 class Status;
166 template <typename T>
167 class StatusOr;
168
169 namespace status_internal {
170 ABSL_ATTRIBUTE_PURE_FUNCTION absl::Nonnull<std::string*> MakeCheckFailString(
171 absl::Nonnull<const absl::Status*> status,
172 absl::Nonnull<const char*> prefix);
173 } // namespace status_internal
174
175 namespace log_internal {
176
177 // Convert a Status or a StatusOr to its underlying status value.
178 //
179 // (This implementation does not require a dep on absl::Status to work.)
AsStatus(const absl::Status & s)180 inline const absl::Status* AsStatus(const absl::Status& s) { return &s; }
181 template <typename T>
AsStatus(const absl::StatusOr<T> & s)182 const absl::Status* AsStatus(const absl::StatusOr<T>& s) {
183 return &s.status();
184 }
185
186 // A helper class for formatting `expr (V1 vs. V2)` in a `CHECK_XX` statement.
187 // See `MakeCheckOpString` for sample usage.
188 class CheckOpMessageBuilder final {
189 public:
190 // Inserts `exprtext` and ` (` to the stream.
191 explicit CheckOpMessageBuilder(const char* exprtext);
192 ~CheckOpMessageBuilder() = default;
193 // For inserting the first variable.
ForVar1()194 std::ostream& ForVar1() { return stream_; }
195 // For inserting the second variable (adds an intermediate ` vs. `).
196 std::ostream& ForVar2();
197 // Get the result (inserts the closing `)`).
198 std::string* NewString();
199
200 private:
201 std::ostringstream stream_;
202 };
203
204 // This formats a value for a failing `CHECK_XX` statement. Ordinarily, it uses
205 // the definition for `operator<<`, with a few special cases below.
206 template <typename T>
MakeCheckOpValueString(std::ostream & os,const T & v)207 inline void MakeCheckOpValueString(std::ostream& os, const T& v) {
208 os << log_internal::NullGuard<T>::Guard(v);
209 }
210
211 // Overloads for char types provide readable values for unprintable characters.
212 void MakeCheckOpValueString(std::ostream& os, char v);
213 void MakeCheckOpValueString(std::ostream& os, signed char v);
214 void MakeCheckOpValueString(std::ostream& os, unsigned char v);
215 void MakeCheckOpValueString(std::ostream& os, const void* p);
216
217 namespace detect_specialization {
218
219 // MakeCheckOpString is being specialized for every T and U pair that is being
220 // passed to the CHECK_op macros. However, there is a lot of redundancy in these
221 // specializations that creates unnecessary library and binary bloat.
222 // The number of instantiations tends to be O(n^2) because we have two
223 // independent inputs. This technique works by reducing `n`.
224 //
225 // Most user-defined types being passed to CHECK_op end up being printed as a
226 // builtin type. For example, enums tend to be implicitly converted to its
227 // underlying type when calling operator<<, and pointers are printed with the
228 // `const void*` overload.
229 // To reduce the number of instantiations we coerce these values before calling
230 // MakeCheckOpString instead of inside it.
231 //
232 // To detect if this coercion is needed, we duplicate all the relevant
233 // operator<< overloads as specified in the standard, just in a different
234 // namespace. If the call to `stream << value` becomes ambiguous, it means that
235 // one of these overloads is the one selected by overload resolution. We then
236 // do overload resolution again just with our overload set to see which one gets
237 // selected. That tells us which type to coerce to.
238 // If the augmented call was not ambiguous, it means that none of these were
239 // selected and we can't coerce the input.
240 //
241 // As a secondary step to reduce code duplication, we promote integral types to
242 // their 64-bit variant. This does not change the printed value, but reduces the
243 // number of instantiations even further. Promoting an integer is very cheap at
244 // the call site.
245 int64_t operator<<(std::ostream&, short value); // NOLINT
246 int64_t operator<<(std::ostream&, unsigned short value); // NOLINT
247 int64_t operator<<(std::ostream&, int value);
248 int64_t operator<<(std::ostream&, unsigned int value);
249 int64_t operator<<(std::ostream&, long value); // NOLINT
250 uint64_t operator<<(std::ostream&, unsigned long value); // NOLINT
251 int64_t operator<<(std::ostream&, long long value); // NOLINT
252 uint64_t operator<<(std::ostream&, unsigned long long value); // NOLINT
253 float operator<<(std::ostream&, float value);
254 double operator<<(std::ostream&, double value);
255 long double operator<<(std::ostream&, long double value);
256 bool operator<<(std::ostream&, bool value);
257 const void* operator<<(std::ostream&, const void* value);
258 const void* operator<<(std::ostream&, std::nullptr_t);
259
260 // These `char` overloads are specified like this in the standard, so we have to
261 // write them exactly the same to ensure the call is ambiguous.
262 // If we wrote it in a different way (eg taking std::ostream instead of the
263 // template) then one call might have a higher rank than the other and it would
264 // not be ambiguous.
265 template <typename Traits>
266 char operator<<(std::basic_ostream<char, Traits>&, char);
267 template <typename Traits>
268 signed char operator<<(std::basic_ostream<char, Traits>&, signed char);
269 template <typename Traits>
270 unsigned char operator<<(std::basic_ostream<char, Traits>&, unsigned char);
271 template <typename Traits>
272 const char* operator<<(std::basic_ostream<char, Traits>&, const char*);
273 template <typename Traits>
274 const signed char* operator<<(std::basic_ostream<char, Traits>&,
275 const signed char*);
276 template <typename Traits>
277 const unsigned char* operator<<(std::basic_ostream<char, Traits>&,
278 const unsigned char*);
279
280 // This overload triggers when the call is not ambiguous.
281 // It means that T is being printed with some overload not on this list.
282 // We keep the value as `const T&`.
283 template <typename T, typename = decltype(std::declval<std::ostream&>()
284 << std::declval<const T&>())>
285 const T& Detect(int);
286
287 // This overload triggers when the call is ambiguous.
288 // It means that T is either one from this list or printed as one from this
289 // list. Eg an enum that decays to `int` for printing.
290 // We ask the overload set to give us the type we want to convert it to.
291 template <typename T>
292 decltype(detect_specialization::operator<<(std::declval<std::ostream&>(),
293 std::declval<const T&>()))
294 Detect(char);
295
296 // A sink for AbslStringify which redirects everything to a std::ostream.
297 class StringifySink {
298 public:
299 explicit StringifySink(std::ostream& os ABSL_ATTRIBUTE_LIFETIME_BOUND);
300
301 void Append(absl::string_view text);
302 void Append(size_t length, char ch);
303 friend void AbslFormatFlush(StringifySink* sink, absl::string_view text);
304
305 private:
306 std::ostream& os_;
307 };
308
309 // Wraps a type implementing AbslStringify, and implements operator<<.
310 template <typename T>
311 class StringifyToStreamWrapper {
312 public:
StringifyToStreamWrapper(const T & v ABSL_ATTRIBUTE_LIFETIME_BOUND)313 explicit StringifyToStreamWrapper(const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND)
314 : v_(v) {}
315
316 friend std::ostream& operator<<(std::ostream& os,
317 const StringifyToStreamWrapper& wrapper) {
318 StringifySink sink(os);
319 AbslStringify(sink, wrapper.v_);
320 return os;
321 }
322
323 private:
324 const T& v_;
325 };
326
327 // This overload triggers when T implements AbslStringify.
328 // StringifyToStreamWrapper is used to allow MakeCheckOpString to use
329 // operator<<.
330 template <typename T>
331 std::enable_if_t<HasAbslStringify<T>::value,
332 StringifyToStreamWrapper<T>>
333 Detect(...); // Ellipsis has lowest preference when int passed.
334 } // namespace detect_specialization
335
336 template <typename T>
337 using CheckOpStreamType = decltype(detect_specialization::Detect<T>(0));
338
339 // Build the error message string. Specify no inlining for code size.
340 template <typename T1, typename T2>
341 ABSL_ATTRIBUTE_RETURNS_NONNULL std::string* MakeCheckOpString(
342 T1 v1, T2 v2, const char* exprtext) ABSL_ATTRIBUTE_NOINLINE;
343
344 template <typename T1, typename T2>
MakeCheckOpString(T1 v1,T2 v2,const char * exprtext)345 std::string* MakeCheckOpString(T1 v1, T2 v2, const char* exprtext) {
346 CheckOpMessageBuilder comb(exprtext);
347 MakeCheckOpValueString(comb.ForVar1(), v1);
348 MakeCheckOpValueString(comb.ForVar2(), v2);
349 return comb.NewString();
350 }
351
352 // Add a few commonly used instantiations as extern to reduce size of objects
353 // files.
354 #define ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(x) \
355 extern template std::string* MakeCheckOpString(x, x, const char*)
356 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(bool);
357 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(int64_t);
358 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(uint64_t);
359 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(float);
360 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(double);
361 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(char);
362 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(unsigned char);
363 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const std::string&);
364 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const absl::string_view&);
365 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const char*);
366 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const signed char*);
367 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const unsigned char*);
368 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const void*);
369 #undef ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN
370
371 // `ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT` skips formatting the Check_OP result
372 // string iff `ABSL_MIN_LOG_LEVEL` exceeds `kFatal`, instead returning an empty
373 // string.
374 #ifdef ABSL_MIN_LOG_LEVEL
375 #define ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2, exprtext) \
376 ((::absl::LogSeverity::kFatal >= \
377 static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL)) \
378 ? MakeCheckOpString<U1, U2>(v1, v2, exprtext) \
379 : new std::string())
380 #else
381 #define ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2, exprtext) \
382 MakeCheckOpString<U1, U2>(v1, v2, exprtext)
383 #endif
384
385 // Helper functions for `ABSL_LOG_INTERNAL_CHECK_OP` macro family. The
386 // `(int, int)` override works around the issue that the compiler will not
387 // instantiate the template version of the function on values of unnamed enum
388 // type.
389 #define ABSL_LOG_INTERNAL_CHECK_OP_IMPL(name, op) \
390 template <typename T1, typename T2> \
391 inline constexpr ::std::string* name##Impl(const T1& v1, const T2& v2, \
392 const char* exprtext) { \
393 using U1 = CheckOpStreamType<T1>; \
394 using U2 = CheckOpStreamType<T2>; \
395 return ABSL_PREDICT_TRUE(v1 op v2) \
396 ? nullptr \
397 : ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, U1(v1), \
398 U2(v2), exprtext); \
399 } \
400 inline constexpr ::std::string* name##Impl(int v1, int v2, \
401 const char* exprtext) { \
402 return name##Impl<int, int>(v1, v2, exprtext); \
403 }
404
405 ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_EQ, ==)
406 ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_NE, !=)
407 ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LE, <=)
408 ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LT, <)
409 ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GE, >=)
410 ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GT, >)
411 #undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT
412 #undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL
413
414 std::string* CheckstrcmptrueImpl(const char* s1, const char* s2,
415 const char* exprtext);
416 std::string* CheckstrcmpfalseImpl(const char* s1, const char* s2,
417 const char* exprtext);
418 std::string* CheckstrcasecmptrueImpl(const char* s1, const char* s2,
419 const char* exprtext);
420 std::string* CheckstrcasecmpfalseImpl(const char* s1, const char* s2,
421 const char* exprtext);
422
423 // `CHECK_EQ` and friends want to pass their arguments by reference, however
424 // this winds up exposing lots of cases where people have defined and
425 // initialized static const data members but never declared them (i.e. in a .cc
426 // file), meaning they are not referenceable. This function avoids that problem
427 // for integers (the most common cases) by overloading for every primitive
428 // integer type, even the ones we discourage, and returning them by value.
429 template <typename T>
GetReferenceableValue(const T & t)430 inline constexpr const T& GetReferenceableValue(const T& t) {
431 return t;
432 }
GetReferenceableValue(char t)433 inline constexpr char GetReferenceableValue(char t) { return t; }
GetReferenceableValue(unsigned char t)434 inline constexpr unsigned char GetReferenceableValue(unsigned char t) {
435 return t;
436 }
GetReferenceableValue(signed char t)437 inline constexpr signed char GetReferenceableValue(signed char t) { return t; }
GetReferenceableValue(short t)438 inline constexpr short GetReferenceableValue(short t) { return t; } // NOLINT
GetReferenceableValue(unsigned short t)439 inline constexpr unsigned short GetReferenceableValue( // NOLINT
440 unsigned short t) { // NOLINT
441 return t;
442 }
GetReferenceableValue(int t)443 inline constexpr int GetReferenceableValue(int t) { return t; }
GetReferenceableValue(unsigned int t)444 inline constexpr unsigned int GetReferenceableValue(unsigned int t) {
445 return t;
446 }
GetReferenceableValue(long t)447 inline constexpr long GetReferenceableValue(long t) { return t; } // NOLINT
GetReferenceableValue(unsigned long t)448 inline constexpr unsigned long GetReferenceableValue( // NOLINT
449 unsigned long t) { // NOLINT
450 return t;
451 }
GetReferenceableValue(long long t)452 inline constexpr long long GetReferenceableValue(long long t) { // NOLINT
453 return t;
454 }
GetReferenceableValue(unsigned long long t)455 inline constexpr unsigned long long GetReferenceableValue( // NOLINT
456 unsigned long long t) { // NOLINT
457 return t;
458 }
459
460 } // namespace log_internal
461 ABSL_NAMESPACE_END
462 } // namespace absl
463
464 #endif // ABSL_LOG_INTERNAL_CHECK_OP_H_
465