Records
Functions
-
int64_t operator<< (std::ostream & , short value)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 & , unsigned short value) -
int64_t operator<< (std::ostream & , int value) -
int64_t operator<< (std::ostream & , unsigned int value) -
int64_t operator<< (std::ostream & , long value) -
uint64_t operator<< (std::ostream & , unsigned long value) -
int64_t operator<< (std::ostream & , long long value) -
uint64_t operator<< (std::ostream & , unsigned long long value) -
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 * _Nullable operator<< (std::ostream & , const void * _Nullable value) -
const void * _Nullable operator<< (std::ostream & , std::nullptr_t ) -
template <typename Traits>char operator<< (std::basic_ostream<char, Traits> & , char )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>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 * _Nonnull operator<< (std::basic_ostream<char, Traits> & , const char * _Nonnull ) -
template <typename Traits>const signed char * _Nonnull operator<< (std::basic_ostream<char, Traits> & , const signed char * _Nonnull ) -
template <typename Traits>const unsigned char * _Nonnull operator<< (std::basic_ostream<char, Traits> & , const unsigned char * _Nonnull ) -
template <typename T, typename = decltype(std::declval<std::ostream&>() << std::declval<const T&>())>const T & Detect (int )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>decltype(detect_specialization::operator<<(std::declval<std::ostream &>(), std::declval<const T &>())) Detect (char )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 unscoped enum that decays to `int` for printing.
We ask the overload set to give us the type we want to convert it to.
-
void AbslFormatFlush (StringifySink * _Nonnull sink, absl::string_view text) -
template <typename T>std::enable_if_t<HasAbslStringify<T>::value, StringifyToStreamWrapper<T>> Detect ()This overload triggers when T implements AbslStringify.
StringifyToStreamWrapper is used to allow MakeCheckOpString to use
operator
<
<
.
-
template <typename T>std::enable_if_t<std::negation_v<std::disjunction<std::is_convertible<T, int>, std::is_enum<T>, std::is_pointer<T>, std::is_same<T, std::nullptr_t>, HasOstreamOperator<T>, HasAbslStringify<T>>>, UnprintableWrapper> Detect ()This overload triggers when T is neither possible to print nor an enum.
-
template <typename T>std::enable_if_t<std::conjunction_v<std::is_enum<T>, std::negation<std::is_convertible<T, int>>, std::negation<HasOstreamOperator<T>>, std::negation<HasAbslStringify<T>>>, std::conditional_t<std::is_same_v<UnderlyingTypeT<T>, bool> || std::is_same_v<UnderlyingTypeT<T>, char> || std::is_same_v<UnderlyingTypeT<T>, signed char> || std::is_same_v<UnderlyingTypeT<T>, unsigned char>, UnderlyingTypeT<T>, std::conditional_t<std::is_signed_v<UnderlyingTypeT<T>>, int64_t, uint64_t>>> Detect ()This overload triggers when T is a scoped enum that has not defined an output
stream operator (operator
<
<
) or AbslStringify. It causes the enum value to be
converted to a type that can be streamed. For consistency with other enums, a
scoped enum backed by a bool or char is converted to its underlying type, and
one backed by another integer is converted to (u)int64_t.
-
std::ostream & operator<< (std::ostream & os, const StringifyToStreamWrapper<T> & wrapper)Defined at line 337 of file ../../third_party/abseil-cpp/src/absl/log/internal/check_op.h