| Index: base/logging.h
|
| diff --git a/base/logging.h b/base/logging.h
|
| index 07674698e0220dbd7d2ab1662556e2853adc275d..40414532344e60488d39f6493abe21b82eb1f63b 100644
|
| --- a/base/logging.h
|
| +++ b/base/logging.h
|
| @@ -11,6 +11,7 @@
|
| #include <cstring>
|
| #include <sstream>
|
| #include <string>
|
| +#include <type_traits>
|
|
|
| #include "base/base_export.h"
|
| #include "base/debug/debugger.h"
|
| @@ -513,14 +514,77 @@ class CheckOpResult {
|
|
|
| #endif
|
|
|
| +namespace internal {
|
| +// Type trait helper to detect scoped enums. Unscoped enums are always
|
| +// implicitly convertible to ints, per C++11 4.7.1:
|
| +// A prvalue of an unscoped enumeration type can be converted to a prvalue of
|
| +// an integer type.
|
| +// while a scoped enum requires an explicit conversion per C++11 5.2.9.9.
|
| +//
|
| +// Note that these helper traits don't use std::integral_constant, because
|
| +// less-than and greater-than operators don't work well inside a template
|
| +// parameter list.
|
| +template <typename T>
|
| +using is_scoped_enum =
|
| + std::integral_constant<bool,
|
| + std::is_enum<T>::value &&
|
| + !std::is_convertible<T, int>::value>;
|
| +
|
| +template <typename T, bool = is_scoped_enum<T>::value>
|
| +struct is_signed_scoped_enum {
|
| + static const bool value = static_cast<T>(-1) < static_cast<T>(0);
|
| +};
|
| +
|
| +template <typename T>
|
| +struct is_signed_scoped_enum<T, false> : std::false_type {};
|
| +
|
| +template <typename T, bool = is_scoped_enum<T>::value>
|
| +struct is_unsigned_scoped_enum {
|
| + static const bool value = static_cast<T>(-1) > static_cast<T>(0);
|
| +};
|
| +
|
| +template <typename T>
|
| +struct is_unsigned_scoped_enum<T, false> : public std::false_type {};
|
| +
|
| +} // namespace internal
|
| +
|
| +// Default value logger just uses operator<<.
|
| +template <class T,
|
| + typename std::enable_if<!internal::is_scoped_enum<T>::value>::type* =
|
| + nullptr>
|
| +void MakeCheckOpValueString(const T& v, std::ostream* os) {
|
| + *os << v;
|
| +}
|
| +
|
| +// TODO(dcheng): Once all platforms support the std::underlying_type trait, use
|
| +// it here. For now, there is no universal library support, so cheat and convert
|
| +// it to the max-width integral type.
|
| +template <class T,
|
| + typename std::enable_if<
|
| + internal::is_signed_scoped_enum<T>::value>::type* = nullptr>
|
| +void MakeCheckOpValueString(const T& v, std::ostream* os) {
|
| + *os << static_cast<intmax_t>(v);
|
| +}
|
| +
|
| +template <class T,
|
| + typename std::enable_if<
|
| + internal::is_unsigned_scoped_enum<T>::value>::type* = nullptr>
|
| +void MakeCheckOpValueString(const T& v, std::ostream* os) {
|
| + *os << static_cast<uintmax_t>(v);
|
| +}
|
| +
|
| // Build the error message string. This is separate from the "Impl"
|
| // function template because it is not performance critical and so can
|
| // be out of line, while the "Impl" code should be inline. Caller
|
| // takes ownership of the returned string.
|
| -template<class t1, class t2>
|
| -std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
|
| +template <class T1, class T2>
|
| +std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* names) {
|
| std::ostringstream ss;
|
| - ss << names << " (" << v1 << " vs. " << v2 << ")";
|
| + ss << names << " (";
|
| + MakeCheckOpValueString(v1, &ss);
|
| + ss << " vs. ";
|
| + MakeCheckOpValueString(v2, &ss);
|
| + ss << ")";
|
| std::string* msg = new std::string(ss.str());
|
| return msg;
|
| }
|
|
|