| 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;
 | 
|  }
 | 
| 
 |