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