0

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:
jbroman
2016-05-25 17:28:46 -07:00
committed by Commit bot
parent 0a94e31bfb
commit 6bcfec42d8
5 changed files with 121 additions and 7 deletions

@@ -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