|
40 | 40 | #include "absl/log/internal/nullstream.h" |
41 | 41 | #include "absl/log/internal/strip.h" |
42 | 42 | #include "absl/strings/has_absl_stringify.h" |
| 43 | +#include "absl/strings/has_ostream_operator.h" |
43 | 44 | #include "absl/strings/string_view.h" |
44 | 45 |
|
45 | 46 | // `ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL` wraps string literals that |
@@ -357,42 +358,45 @@ std::enable_if_t<HasAbslStringify<T>::value, |
357 | 358 | StringifyToStreamWrapper<T>> |
358 | 359 | Detect(...); // Ellipsis has lowest preference when int passed. |
359 | 360 |
|
360 | | -// is_streamable is true for types that have an output stream operator<<. |
361 | | -template <class T, class = void> |
362 | | -struct is_streamable : std::false_type {}; |
363 | | - |
364 | | -template <class T> |
365 | | -struct is_streamable<T, std::void_t<decltype(std::declval<std::ostream&>() |
366 | | - << std::declval<T>())>> |
367 | | - : std::true_type {}; |
368 | | - |
369 | 361 | // This overload triggers when T is neither possible to print nor an enum. |
370 | 362 | template <typename T> |
371 | 363 | std::enable_if_t<std::negation_v<std::disjunction< |
372 | 364 | std::is_convertible<T, int>, std::is_enum<T>, |
373 | 365 | std::is_pointer<T>, std::is_same<T, std::nullptr_t>, |
374 | | - is_streamable<T>, HasAbslStringify<T>>>, |
| 366 | + HasOstreamOperator<T>, HasAbslStringify<T>>>, |
375 | 367 | UnprintableWrapper> |
376 | 368 | Detect(...); |
377 | 369 |
|
| 370 | +// Equivalent to the updated std::underlying_type from C++20, which is no |
| 371 | +// longer undefined behavior for non-enum types. |
| 372 | +template <typename T, typename EnableT = void> |
| 373 | +struct UnderlyingType {}; |
| 374 | + |
| 375 | +template <typename T> |
| 376 | +struct UnderlyingType<T, std::enable_if_t<std::is_enum_v<T>>> { |
| 377 | + using type = std::underlying_type_t<T>; |
| 378 | +}; |
| 379 | +template <typename T> |
| 380 | +using UnderlyingTypeT = typename UnderlyingType<T>::type; |
| 381 | + |
378 | 382 | // This overload triggers when T is a scoped enum that has not defined an output |
379 | 383 | // stream operator (operator<<) or AbslStringify. It causes the enum value to be |
380 | 384 | // converted to a type that can be streamed. For consistency with other enums, a |
381 | 385 | // scoped enum backed by a bool or char is converted to its underlying type, and |
382 | 386 | // one backed by another integer is converted to (u)int64_t. |
383 | 387 | template <typename T> |
384 | 388 | std::enable_if_t< |
385 | | - std::conjunction_v< |
386 | | - std::is_enum<T>, std::negation<std::is_convertible<T, int>>, |
387 | | - std::negation<is_streamable<T>>, std::negation<HasAbslStringify<T>>>, |
388 | | - std::conditional_t< |
389 | | - std::is_same_v<std::underlying_type_t<T>, bool> || |
390 | | - std::is_same_v<std::underlying_type_t<T>, char> || |
391 | | - std::is_same_v<std::underlying_type_t<T>, signed char> || |
392 | | - std::is_same_v<std::underlying_type_t<T>, unsigned char>, |
393 | | - std::underlying_type_t<T>, |
394 | | - std::conditional_t<std::is_signed_v<std::underlying_type_t<T>>, int64_t, |
395 | | - uint64_t>>> |
| 389 | + std::conjunction_v<std::is_enum<T>, |
| 390 | + std::negation<std::is_convertible<T, int>>, |
| 391 | + std::negation<HasOstreamOperator<T>>, |
| 392 | + std::negation<HasAbslStringify<T>>>, |
| 393 | + std::conditional_t<std::is_same_v<UnderlyingTypeT<T>, bool> || |
| 394 | + std::is_same_v<UnderlyingTypeT<T>, char> || |
| 395 | + std::is_same_v<UnderlyingTypeT<T>, signed char> || |
| 396 | + std::is_same_v<UnderlyingTypeT<T>, unsigned char>, |
| 397 | + UnderlyingTypeT<T>, |
| 398 | + std::conditional_t<std::is_signed_v<UnderlyingTypeT<T>>, |
| 399 | + int64_t, uint64_t>>> |
396 | 400 | Detect(...); |
397 | 401 | } // namespace detect_specialization |
398 | 402 |
|
|
0 commit comments