base: Support using (D)CHECK on scoped enums.
BUG=555314 Review-Url: https://codereview.chromium.org/2001783002 Cr-Commit-Position: refs/heads/master@{#396052}
This commit is contained in:
@@ -454,8 +454,7 @@ template std::string* MakeCheckOpString<unsigned int, unsigned long>(
|
||||
template std::string* MakeCheckOpString<std::string, std::string>(
|
||||
const std::string&, const std::string&, const char* name);
|
||||
|
||||
template <>
|
||||
void MakeCheckOpValueString(std::ostream* os, const std::nullptr_t& p) {
|
||||
void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p) {
|
||||
(*os) << "nullptr";
|
||||
}
|
||||
|
||||
|
@@ -11,10 +11,13 @@
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/debug/debugger.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/template_util.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
//
|
||||
@@ -522,14 +525,26 @@ class CheckOpResult {
|
||||
// This formats a value for a failing CHECK_XX statement. Ordinarily,
|
||||
// it uses the definition for operator<<, with a few special cases below.
|
||||
template <typename T>
|
||||
inline void MakeCheckOpValueString(std::ostream* os, const T& v) {
|
||||
inline typename std::enable_if<
|
||||
base::internal::SupportsOstreamOperator<const T&>::value,
|
||||
void>::type
|
||||
MakeCheckOpValueString(std::ostream* os, const T& v) {
|
||||
(*os) << v;
|
||||
}
|
||||
|
||||
// We need an explicit specialization for std::nullptr_t.
|
||||
template <>
|
||||
BASE_EXPORT void MakeCheckOpValueString(std::ostream* os,
|
||||
const std::nullptr_t& p);
|
||||
// We need overloads for enums that don't support operator<<.
|
||||
// (i.e. scoped enums where no operator<< overload was declared).
|
||||
template <typename T>
|
||||
inline typename std::enable_if<
|
||||
!base::internal::SupportsOstreamOperator<const T&>::value &&
|
||||
std::is_enum<T>::value,
|
||||
void>::type
|
||||
MakeCheckOpValueString(std::ostream* os, const T& v) {
|
||||
(*os) << static_cast<typename base::underlying_type<T>::type>(v);
|
||||
}
|
||||
|
||||
// We need an explicit overload for std::nullptr_t.
|
||||
BASE_EXPORT void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p);
|
||||
|
||||
// Build the error message string. This is separate from the "Impl"
|
||||
// function template because it is not performance critical and so can
|
||||
|
@@ -251,6 +251,13 @@ TEST_F(LoggingTest, Dcheck) {
|
||||
DCHECK_NE(p_not_null, nullptr);
|
||||
DCHECK_NE(nullptr, p_not_null);
|
||||
EXPECT_EQ(0, log_sink_call_count);
|
||||
|
||||
// Test DCHECK on a scoped enum.
|
||||
enum class Animal { DOG, CAT };
|
||||
DCHECK_EQ(Animal::DOG, Animal::DOG);
|
||||
EXPECT_EQ(0, log_sink_call_count);
|
||||
DCHECK_EQ(Animal::DOG, Animal::CAT);
|
||||
EXPECT_EQ(DCHECK_IS_ON() ? 1 : 0, log_sink_call_count);
|
||||
}
|
||||
|
||||
TEST_F(LoggingTest, DcheckReleaseBehavior) {
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#define BASE_TEMPLATE_UTIL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <iosfwd>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -57,6 +58,15 @@ struct IsAssignableImpl
|
||||
template <class Lvalue, class Rvalue>
|
||||
struct IsAssignableImpl<Lvalue, Rvalue, true> : public std::false_type {};
|
||||
|
||||
// Uses expression SFINAE to detect whether using operator<< would work.
|
||||
template <typename T, typename = void>
|
||||
struct SupportsOstreamOperator : std::false_type {};
|
||||
template <typename T>
|
||||
struct SupportsOstreamOperator<T,
|
||||
decltype(void(std::declval<std::ostream&>()
|
||||
<< std::declval<T>()))>
|
||||
: std::true_type {};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// TODO(crbug.com/554293): Remove this when all platforms have this in the std
|
||||
@@ -82,6 +92,26 @@ struct is_move_assignable
|
||||
const typename std::add_rvalue_reference<T>::type> {
|
||||
};
|
||||
|
||||
// underlying_type produces the integer type backing an enum type.
|
||||
// This hacks around libstdc++ 4.6 missing std::underlying_type, while we need
|
||||
// to support it.
|
||||
// TODO(crbug.com/554293): Remove this when all platforms have this in the std
|
||||
// namespace.
|
||||
#define CR_GLIBCXX_4_7_0 20120322
|
||||
#define CR_GLIBCXX_4_5_4 20120702
|
||||
#define CR_GLIBCXX_4_6_4 20121127
|
||||
#if defined(__GLIBCXX__) && \
|
||||
(__GLIBCXX__ < CR_GLIBCXX_4_7_0 || __GLIBCXX__ == CR_GLIBCXX_4_5_4 || \
|
||||
__GLIBCXX__ == CR_GLIBCXX_4_6_4)
|
||||
template <typename T>
|
||||
struct underlying_type {
|
||||
using type = __underlying_type(T);
|
||||
};
|
||||
#else
|
||||
template <typename T>
|
||||
using underlying_type = std::underlying_type<T>;
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_TEMPLATE_UTIL_H_
|
||||
|
@@ -4,11 +4,26 @@
|
||||
|
||||
#include "base/template_util.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
enum SimpleEnum { SIMPLE_ENUM };
|
||||
enum EnumWithExplicitType : uint64_t { ENUM_WITH_EXPLICIT_TYPE };
|
||||
enum class ScopedEnum { SCOPED_ENUM };
|
||||
enum class ScopedEnumWithOperator { SCOPED_ENUM_WITH_OPERATOR };
|
||||
std::ostream& operator<<(std::ostream& os, ScopedEnumWithOperator v) {
|
||||
return os;
|
||||
}
|
||||
struct SimpleStruct {};
|
||||
struct StructWithOperator {};
|
||||
std::ostream& operator<<(std::ostream& os, const StructWithOperator& v) {
|
||||
return os;
|
||||
}
|
||||
|
||||
// is_non_const_reference<Type>
|
||||
static_assert(!is_non_const_reference<int>::value, "IsNonConstReference");
|
||||
static_assert(!is_non_const_reference<const int&>::value,
|
||||
@@ -48,5 +63,53 @@ static_assert(is_move_assignable<AssignCopy>::value, "IsMoveAssignable");
|
||||
static_assert(is_move_assignable<AssignNoCopy>::value, "IsMoveAssignable");
|
||||
static_assert(!is_move_assignable<AssignNoMove>::value, "IsMoveAssignable");
|
||||
|
||||
// A few standard types that definitely support printing.
|
||||
static_assert(internal::SupportsOstreamOperator<int>::value,
|
||||
"ints should be printable");
|
||||
static_assert(internal::SupportsOstreamOperator<const char*>::value,
|
||||
"C strings should be printable");
|
||||
static_assert(internal::SupportsOstreamOperator<std::string>::value,
|
||||
"std::string should be printable");
|
||||
|
||||
// Various kinds of enums operator<< support.
|
||||
static_assert(internal::SupportsOstreamOperator<SimpleEnum>::value,
|
||||
"simple enum should be printable by value");
|
||||
static_assert(internal::SupportsOstreamOperator<const SimpleEnum&>::value,
|
||||
"simple enum should be printable by const ref");
|
||||
static_assert(internal::SupportsOstreamOperator<EnumWithExplicitType>::value,
|
||||
"enum with explicit type should be printable by value");
|
||||
static_assert(
|
||||
internal::SupportsOstreamOperator<const EnumWithExplicitType&>::value,
|
||||
"enum with explicit type should be printable by const ref");
|
||||
static_assert(!internal::SupportsOstreamOperator<ScopedEnum>::value,
|
||||
"scoped enum should not be printable by value");
|
||||
static_assert(!internal::SupportsOstreamOperator<const ScopedEnum&>::value,
|
||||
"simple enum should not be printable by const ref");
|
||||
static_assert(internal::SupportsOstreamOperator<ScopedEnumWithOperator>::value,
|
||||
"scoped enum with operator<< should be printable by value");
|
||||
static_assert(
|
||||
internal::SupportsOstreamOperator<const ScopedEnumWithOperator&>::value,
|
||||
"scoped enum with operator<< should be printable by const ref");
|
||||
|
||||
// operator<< support on structs.
|
||||
static_assert(!internal::SupportsOstreamOperator<SimpleStruct>::value,
|
||||
"simple struct should not be printable by value");
|
||||
static_assert(!internal::SupportsOstreamOperator<const SimpleStruct&>::value,
|
||||
"simple struct should not be printable by const ref");
|
||||
static_assert(internal::SupportsOstreamOperator<StructWithOperator>::value,
|
||||
"struct with operator<< should be printable by value");
|
||||
static_assert(
|
||||
internal::SupportsOstreamOperator<const StructWithOperator&>::value,
|
||||
"struct with operator<< should be printable by const ref");
|
||||
|
||||
// underlying type of enums
|
||||
static_assert(std::is_integral<underlying_type<SimpleEnum>::type>::value,
|
||||
"simple enum must have some integral type");
|
||||
static_assert(
|
||||
std::is_same<underlying_type<EnumWithExplicitType>::type, uint64_t>::value,
|
||||
"explicit type must be detected");
|
||||
static_assert(std::is_same<underlying_type<ScopedEnum>::type, int>::value,
|
||||
"scoped enum defaults to int");
|
||||
|
||||
} // namespace
|
||||
} // namespace base
|
||||
|
Reference in New Issue
Block a user