Use overloaded operator<< for enums if available (#232)

This commit is contained in:
vitaut 2015-11-24 08:18:19 -08:00
parent 1a2a333a1a
commit 6cff6d8137
3 changed files with 69 additions and 20 deletions

View File

@ -937,28 +937,59 @@ typedef char No[2];
// These are non-members to workaround an overload resolution bug in bcc32. // These are non-members to workaround an overload resolution bug in bcc32.
Yes &convert(fmt::ULongLong); Yes &convert(fmt::ULongLong);
Yes &convert(std::ostream &);
No &convert(...); No &convert(...);
template <typename T> template <typename T>
class IsConvertibleToInt { T &get();
protected:
static const T &get();
public: struct DummyStream : std::ostream {
enum { value = (sizeof(convert(get())) == sizeof(Yes)) }; // Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
}; };
#define FMT_CONVERTIBLE_TO_INT(Type) \ No &operator<<(std::ostream &, int);
template<typename T, bool ENABLE_CONVERSION>
struct ConvertToIntImpl {
enum { value = false };
};
template<typename T>
struct ConvertToIntImpl<T, true> {
// Convert to int only if T doesn't have an overloaded operator<<.
enum {
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
template<typename T, bool ENABLE_CONVERSION>
struct ConvertToIntImpl2 {
enum { value = false };
};
template<typename T>
struct ConvertToIntImpl2<T, true> {
enum {
// Don't convert numeric types.
value = ConvertToIntImpl<T, !std::numeric_limits<T>::is_specialized>::value
};
};
template<typename T>
struct ConvertToInt {
enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
enum { value = ConvertToIntImpl2<T, enable_conversion>::value };
};
#define FMT_DISABLE_CONVERSION_TO_INT(Type) \
template <> \ template <> \
class IsConvertibleToInt<Type> { \ struct ConvertToInt<Type> { enum { value = 0 }; }
public: \
enum { value = 1 }; \
}
// Silence warnings about convering float to int. // Silence warnings about convering float to int.
FMT_CONVERTIBLE_TO_INT(float); FMT_DISABLE_CONVERSION_TO_INT(float);
FMT_CONVERTIBLE_TO_INT(double); FMT_DISABLE_CONVERSION_TO_INT(double);
FMT_CONVERTIBLE_TO_INT(long double); FMT_DISABLE_CONVERSION_TO_INT(long double);
template<bool B, class T = void> template<bool B, class T = void>
struct EnableIf {}; struct EnableIf {};
@ -1113,20 +1144,20 @@ class MakeValue : public Arg {
template <typename T> template <typename T>
MakeValue(const T &value, MakeValue(const T &value,
typename EnableIf<Not< typename EnableIf<Not<
IsConvertibleToInt<T>::value>::value, int>::type = 0) { ConvertToInt<T>::value>::value, int>::type = 0) {
custom.value = &value; custom.value = &value;
custom.format = &format_custom_arg<T>; custom.format = &format_custom_arg<T>;
} }
template <typename T> template <typename T>
MakeValue(const T &value, MakeValue(const T &value,
typename EnableIf<IsConvertibleToInt<T>::value, int>::type = 0) { typename EnableIf<ConvertToInt<T>::value, int>::type = 0) {
int_value = value; int_value = value;
} }
template <typename T> template <typename T>
static uint64_t type(const T &) { static uint64_t type(const T &) {
return IsConvertibleToInt<T>::value ? Arg::INT : Arg::CUSTOM; return ConvertToInt<T>::value ? Arg::INT : Arg::CUSTOM;
} }
// Additional template param `Char_` is needed here because make_type always // Additional template param `Char_` is needed here because make_type always

View File

@ -1637,3 +1637,15 @@ TEST(LiteralsTest, NamedArg) {
udl_a_w); udl_a_w);
} }
#endif // FMT_USE_USER_DEFINED_LITERALS #endif // FMT_USE_USER_DEFINED_LITERALS
enum TestEnum {};
std::ostream &operator<<(std::ostream &os, TestEnum) {
return os << "TestEnum";
}
enum TestEnum2 { A };
TEST(FormatTest, Enum) {
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
EXPECT_EQ("0", fmt::format("{}", A));
}

View File

@ -870,15 +870,21 @@ TEST(UtilTest, ReportWindowsError) {
#endif // _WIN32 #endif // _WIN32
TEST(UtilTest, IsConvertibleToInt) { enum TestEnum2 {};
EXPECT_TRUE(fmt::internal::IsConvertibleToInt<char>::value); enum TestEnum3 {};
EXPECT_FALSE(fmt::internal::IsConvertibleToInt<const char *>::value); std::ostream &operator<<(std::ostream &, TestEnum3);
TEST(UtilTest, ConvertToInt) {
EXPECT_TRUE(fmt::internal::ConvertToInt<char>::enable_conversion);
EXPECT_FALSE(fmt::internal::ConvertToInt<const char *>::enable_conversion);
EXPECT_TRUE(fmt::internal::ConvertToInt<TestEnum2>::value);
EXPECT_FALSE(fmt::internal::ConvertToInt<TestEnum3>::value);
} }
#if FMT_USE_ENUM_BASE #if FMT_USE_ENUM_BASE
enum TestEnum : char {TestValue}; enum TestEnum : char {TestValue};
TEST(UtilTest, IsEnumConvertibleToInt) { TEST(UtilTest, IsEnumConvertibleToInt) {
EXPECT_TRUE(fmt::internal::IsConvertibleToInt<TestEnum>::value); EXPECT_TRUE(fmt::internal::ConvertToInt<TestEnum>::enable_conversion);
} }
#endif #endif