diff options
Diffstat (limited to 'absl/log/internal/check_op.h')
-rw-r--r-- | absl/log/internal/check_op.h | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/absl/log/internal/check_op.h b/absl/log/internal/check_op.h new file mode 100644 index 00000000..4907b89b --- /dev/null +++ b/absl/log/internal/check_op.h @@ -0,0 +1,392 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: log/internal/check_op.h +// ----------------------------------------------------------------------------- +// +// This file declares helpers routines and macros used to implement `CHECK` +// macros. + +#ifndef ABSL_LOG_INTERNAL_CHECK_OP_H_ +#define ABSL_LOG_INTERNAL_CHECK_OP_H_ + +#include <stdint.h> + +#include <ostream> +#include <sstream> +#include <string> +#include <utility> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/log/internal/nullguard.h" +#include "absl/log/internal/nullstream.h" +#include "absl/log/internal/strip.h" + +// `ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL` wraps string literals that +// should be stripped when `ABSL_MIN_LOG_LEVEL` exceeds `kFatal`. +#ifdef ABSL_MIN_LOG_LEVEL +#define ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(literal) \ + (::absl::LogSeverity::kFatal >= \ + static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) \ + ? (literal) \ + : "") +#else +#define ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(literal) (literal) +#endif + +#ifdef NDEBUG +// `NDEBUG` is defined, so `DCHECK_EQ(x, y)` and so on do nothing. However, we +// still want the compiler to parse `x` and `y`, because we don't want to lose +// potentially useful errors and warnings. +#define ABSL_LOG_INTERNAL_DCHECK_NOP(x, y) \ + while (false && ((void)(x), (void)(y), 0)) \ + ::absl::log_internal::NullStream().InternalStream() +#endif + +#define ABSL_LOG_INTERNAL_CHECK_OP(name, op, val1, val1_text, val2, val2_text) \ + while ( \ + ::std::string* absl_log_internal_check_op_result ABSL_ATTRIBUTE_UNUSED = \ + ::absl::log_internal::name##Impl( \ + ::absl::log_internal::GetReferenceableValue(val1), \ + ::absl::log_internal::GetReferenceableValue(val2), \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val1_text \ + " " #op " " val2_text))) \ + ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_op_result).InternalStream() +#define ABSL_LOG_INTERNAL_QCHECK_OP(name, op, val1, val1_text, val2, \ + val2_text) \ + while (::std::string* absl_log_internal_qcheck_op_result = \ + ::absl::log_internal::name##Impl( \ + ::absl::log_internal::GetReferenceableValue(val1), \ + ::absl::log_internal::GetReferenceableValue(val2), \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL( \ + val1_text " " #op " " val2_text))) \ + ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_op_result).InternalStream() +#define ABSL_LOG_INTERNAL_CHECK_STROP(func, op, expected, s1, s1_text, s2, \ + s2_text) \ + while (::std::string* absl_log_internal_check_strop_result = \ + ::absl::log_internal::Check##func##expected##Impl( \ + (s1), (s2), \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op \ + " " s2_text))) \ + ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_strop_result) \ + .InternalStream() +#define ABSL_LOG_INTERNAL_QCHECK_STROP(func, op, expected, s1, s1_text, s2, \ + s2_text) \ + while (::std::string* absl_log_internal_qcheck_strop_result = \ + ::absl::log_internal::Check##func##expected##Impl( \ + (s1), (s2), \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op \ + " " s2_text))) \ + ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_strop_result) \ + .InternalStream() +// This one is tricky: +// * We must evaluate `val` exactly once, yet we need to do two things with it: +// evaluate `.ok()` and (sometimes) `.ToString()`. +// * `val` might be an `absl::Status` or some `absl::StatusOr<T>`. +// * `val` might be e.g. `ATemporary().GetStatus()`, which may return a +// reference to a member of `ATemporary` that is only valid until the end of +// the full expression. +// * We don't want this file to depend on `absl::Status` `#include`s or linkage, +// nor do we want to move the definition to status and introduce a dependency +// in the other direction. We can be assured that callers must already have a +// `Status` and the necessary `#include`s and linkage. +// * Callsites should be small and fast (at least when `val.ok()`): one branch, +// minimal stack footprint. +// * In particular, the string concat stuff should be out-of-line and emitted +// in only one TU to save linker input size +// * We want the `val.ok()` check inline so static analyzers and optimizers can +// see it. +// * As usual, no braces so we can stream into the expansion with `operator<<`. +// * Also as usual, it must expand to a single (partial) statement with no +// ambiguous-else problems. +#define ABSL_LOG_INTERNAL_CHECK_OK(val, val_text) \ + for (::std::pair<const ::absl::Status*, ::std::string*> \ + absl_log_internal_check_ok_goo; \ + absl_log_internal_check_ok_goo.first = \ + ::absl::log_internal::AsStatus(val), \ + absl_log_internal_check_ok_goo.second = \ + ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \ + ? nullptr \ + : ::absl::status_internal::MakeCheckFailString( \ + absl_log_internal_check_ok_goo.first, \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text \ + " is OK")), \ + !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \ + ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_ok_goo.second) \ + .InternalStream() +#define ABSL_LOG_INTERNAL_QCHECK_OK(val, val_text) \ + for (::std::pair<const ::absl::Status*, ::std::string*> \ + absl_log_internal_check_ok_goo; \ + absl_log_internal_check_ok_goo.first = \ + ::absl::log_internal::AsStatus(val), \ + absl_log_internal_check_ok_goo.second = \ + ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \ + ? nullptr \ + : ::absl::status_internal::MakeCheckFailString( \ + absl_log_internal_check_ok_goo.first, \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text \ + " is OK")), \ + !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \ + ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_check_ok_goo.second) \ + .InternalStream() + +namespace absl { +ABSL_NAMESPACE_BEGIN + +class Status; +template <typename T> +class StatusOr; + +namespace status_internal { +std::string* MakeCheckFailString(const absl::Status* status, + const char* prefix); +} // namespace status_internal + +namespace log_internal { + +// Convert a Status or a StatusOr to its underlying status value. +// +// (This implementation does not require a dep on absl::Status to work.) +inline const absl::Status* AsStatus(const absl::Status& s) { return &s; } +template <typename T> +const absl::Status* AsStatus(const absl::StatusOr<T>& s) { + return &s.status(); +} + +// A helper class for formatting `expr (V1 vs. V2)` in a `CHECK_XX` statement. +// See `MakeCheckOpString` for sample usage. +class CheckOpMessageBuilder final { + public: + // Inserts `exprtext` and ` (` to the stream. + explicit CheckOpMessageBuilder(const char* exprtext); + ~CheckOpMessageBuilder() = default; + // For inserting the first variable. + std::ostream& ForVar1() { return stream_; } + // For inserting the second variable (adds an intermediate ` vs. `). + std::ostream& ForVar2(); + // Get the result (inserts the closing `)`). + std::string* NewString(); + + private: + std::ostringstream stream_; +}; + +// This formats a value for a failing `CHECK_XX` statement. Ordinarily, it uses +// the definition for `operator<<`, with a few special cases below. +template <typename T> +inline void MakeCheckOpValueString(std::ostream& os, const T& v) { + os << log_internal::NullGuard<T>::Guard(v); +} + +// Overloads for char types provide readable values for unprintable characters. +void MakeCheckOpValueString(std::ostream& os, char v); +void MakeCheckOpValueString(std::ostream& os, signed char v); +void MakeCheckOpValueString(std::ostream& os, unsigned char v); +void MakeCheckOpValueString(std::ostream& os, const void* p); + +namespace detect_specialization { + +// MakeCheckOpString is being specialized for every T and U pair that is being +// passed to the CHECK_op macros. However, there is a lot of redundancy in these +// specializations that creates unnecessary library and binary bloat. +// The number of instantiations tends to be O(n^2) because we have two +// independent inputs. This technique works by reducing `n`. +// +// Most user-defined types being passed to CHECK_op end up being printed as a +// builtin type. For example, enums tend to be implicitly converted to its +// underlying type when calling operator<<, and pointers are printed with the +// `const void*` overload. +// To reduce the number of instantiations we coerce these values before calling +// MakeCheckOpString instead of inside it. +// +// To detect if this coercion is needed, we duplicate all the relevant +// operator<< overloads as specified in the standard, just in a different +// namespace. If the call to `stream << value` becomes ambiguous, it means that +// one of these overloads is the one selected by overload resolution. We then +// do overload resolution again just with our overload set to see which one gets +// selected. That tells us which type to coerce to. +// If the augmented call was not ambiguous, it means that none of these were +// selected and we can't coerce the input. +// +// As a secondary step to reduce code duplication, we promote integral types to +// their 64-bit variant. This does not change the printed value, but reduces the +// number of instantiations even further. Promoting an integer is very cheap at +// the call site. +int64_t operator<<(std::ostream&, short value); // NOLINT +int64_t operator<<(std::ostream&, unsigned short value); // NOLINT +int64_t operator<<(std::ostream&, int value); +int64_t operator<<(std::ostream&, unsigned int value); +int64_t operator<<(std::ostream&, long value); // NOLINT +uint64_t operator<<(std::ostream&, unsigned long value); // NOLINT +int64_t operator<<(std::ostream&, long long value); // NOLINT +uint64_t operator<<(std::ostream&, unsigned long long value); // NOLINT +float operator<<(std::ostream&, float value); +double operator<<(std::ostream&, double value); +long double operator<<(std::ostream&, long double value); +bool operator<<(std::ostream&, bool value); +const void* operator<<(std::ostream&, const void* value); +const void* operator<<(std::ostream&, std::nullptr_t); + +// These `char` overloads are specified like this in the standard, so we have to +// write them exactly the same to ensure the call is ambiguous. +// If we wrote it in a different way (eg taking std::ostream instead of the +// template) then one call might have a higher rank than the other and it would +// not be ambiguous. +template <typename Traits> +char operator<<(std::basic_ostream<char, Traits>&, char); +template <typename Traits> +signed char operator<<(std::basic_ostream<char, Traits>&, signed char); +template <typename Traits> +unsigned char operator<<(std::basic_ostream<char, Traits>&, unsigned char); +template <typename Traits> +const char* operator<<(std::basic_ostream<char, Traits>&, const char*); +template <typename Traits> +const signed char* operator<<(std::basic_ostream<char, Traits>&, + const signed char*); +template <typename Traits> +const unsigned char* operator<<(std::basic_ostream<char, Traits>&, + const unsigned char*); + +// This overload triggers when the call is not ambiguous. +// It means that T is being printed with some overload not on this list. +// We keep the value as `const T&`. +template <typename T, typename = decltype(std::declval<std::ostream&>() + << std::declval<const T&>())> +const T& Detect(int); + +// This overload triggers when the call is ambiguous. +// It means that T is either one from this list or printed as one from this +// list. Eg an enum that decays to `int` for printing. +// We ask the overload set to give us the type we want to convert it to. +template <typename T> +decltype(detect_specialization::operator<<(std::declval<std::ostream&>(), + std::declval<const T&>())) +Detect(char); + +} // namespace detect_specialization + +template <typename T> +using CheckOpStreamType = decltype(detect_specialization::Detect<T>(0)); + +// Build the error message string. Specify no inlining for code size. +template <typename T1, typename T2> +ABSL_ATTRIBUTE_RETURNS_NONNULL std::string* MakeCheckOpString( + T1 v1, T2 v2, const char* exprtext) ABSL_ATTRIBUTE_NOINLINE; + +template <typename T1, typename T2> +std::string* MakeCheckOpString(T1 v1, T2 v2, const char* exprtext) { + CheckOpMessageBuilder comb(exprtext); + MakeCheckOpValueString(comb.ForVar1(), v1); + MakeCheckOpValueString(comb.ForVar2(), v2); + return comb.NewString(); +} + +// Add a few commonly used instantiations as extern to reduce size of objects +// files. +#define ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(x) \ + extern template std::string* MakeCheckOpString(x, x, const char*) +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(bool); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(int64_t); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(uint64_t); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(float); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(double); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(char); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(unsigned char); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const std::string&); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const absl::string_view&); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const char*); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const signed char*); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const unsigned char*); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const void*); +#undef ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN + +// Helper functions for `ABSL_LOG_INTERNAL_CHECK_OP` macro family. The +// `(int, int)` override works around the issue that the compiler will not +// instantiate the template version of the function on values of unnamed enum +// type. +#define ABSL_LOG_INTERNAL_CHECK_OP_IMPL(name, op) \ + template <typename T1, typename T2> \ + inline constexpr ::std::string* name##Impl(const T1& v1, const T2& v2, \ + const char* exprtext) { \ + using U1 = CheckOpStreamType<T1>; \ + using U2 = CheckOpStreamType<T2>; \ + return ABSL_PREDICT_TRUE(v1 op v2) \ + ? nullptr \ + : MakeCheckOpString<U1, U2>(v1, v2, exprtext); \ + } \ + inline constexpr ::std::string* name##Impl(int v1, int v2, \ + const char* exprtext) { \ + return name##Impl<int, int>(v1, v2, exprtext); \ + } + +ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_EQ, ==) +ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_NE, !=) +ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LE, <=) +ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LT, <) +ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GE, >=) +ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GT, >) +#undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL + +std::string* CheckstrcmptrueImpl(const char* s1, const char* s2, + const char* exprtext); +std::string* CheckstrcmpfalseImpl(const char* s1, const char* s2, + const char* exprtext); +std::string* CheckstrcasecmptrueImpl(const char* s1, const char* s2, + const char* exprtext); +std::string* CheckstrcasecmpfalseImpl(const char* s1, const char* s2, + const char* exprtext); + +// `CHECK_EQ` and friends want to pass their arguments by reference, however +// this winds up exposing lots of cases where people have defined and +// initialized static const data members but never declared them (i.e. in a .cc +// file), meaning they are not referenceable. This function avoids that problem +// for integers (the most common cases) by overloading for every primitive +// integer type, even the ones we discourage, and returning them by value. +template <typename T> +inline constexpr const T& GetReferenceableValue(const T& t) { + return t; +} +inline constexpr char GetReferenceableValue(char t) { return t; } +inline constexpr unsigned char GetReferenceableValue(unsigned char t) { + return t; +} +inline constexpr signed char GetReferenceableValue(signed char t) { return t; } +inline constexpr short GetReferenceableValue(short t) { return t; } // NOLINT +inline constexpr unsigned short GetReferenceableValue( // NOLINT + unsigned short t) { // NOLINT + return t; +} +inline constexpr int GetReferenceableValue(int t) { return t; } +inline unsigned int GetReferenceableValue(unsigned int t) { return t; } +inline constexpr long GetReferenceableValue(long t) { return t; } // NOLINT +inline constexpr unsigned long GetReferenceableValue( // NOLINT + unsigned long t) { // NOLINT + return t; +} +inline constexpr long long GetReferenceableValue(long long t) { // NOLINT + return t; +} +inline constexpr unsigned long long GetReferenceableValue( // NOLINT + unsigned long long t) { // NOLINT + return t; +} + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_CHECK_OP_H_ |