Use overloaded operator<< for enums if available (#232)
This commit is contained in:
parent
1a2a333a1a
commit
6cff6d8137
63
format.h
63
format.h
@ -937,28 +937,59 @@ typedef char No[2];
|
||||
|
||||
// These are non-members to workaround an overload resolution bug in bcc32.
|
||||
Yes &convert(fmt::ULongLong);
|
||||
Yes &convert(std::ostream &);
|
||||
No &convert(...);
|
||||
|
||||
template <typename T>
|
||||
class IsConvertibleToInt {
|
||||
protected:
|
||||
static const T &get();
|
||||
T &get();
|
||||
|
||||
public:
|
||||
enum { value = (sizeof(convert(get())) == sizeof(Yes)) };
|
||||
struct DummyStream : std::ostream {
|
||||
// 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 <> \
|
||||
class IsConvertibleToInt<Type> { \
|
||||
public: \
|
||||
enum { value = 1 }; \
|
||||
}
|
||||
struct ConvertToInt<Type> { enum { value = 0 }; }
|
||||
|
||||
// Silence warnings about convering float to int.
|
||||
FMT_CONVERTIBLE_TO_INT(float);
|
||||
FMT_CONVERTIBLE_TO_INT(double);
|
||||
FMT_CONVERTIBLE_TO_INT(long double);
|
||||
FMT_DISABLE_CONVERSION_TO_INT(float);
|
||||
FMT_DISABLE_CONVERSION_TO_INT(double);
|
||||
FMT_DISABLE_CONVERSION_TO_INT(long double);
|
||||
|
||||
template<bool B, class T = void>
|
||||
struct EnableIf {};
|
||||
@ -1113,20 +1144,20 @@ class MakeValue : public Arg {
|
||||
template <typename T>
|
||||
MakeValue(const T &value,
|
||||
typename EnableIf<Not<
|
||||
IsConvertibleToInt<T>::value>::value, int>::type = 0) {
|
||||
ConvertToInt<T>::value>::value, int>::type = 0) {
|
||||
custom.value = &value;
|
||||
custom.format = &format_custom_arg<T>;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MakeValue(const T &value,
|
||||
typename EnableIf<IsConvertibleToInt<T>::value, int>::type = 0) {
|
||||
typename EnableIf<ConvertToInt<T>::value, int>::type = 0) {
|
||||
int_value = value;
|
||||
}
|
||||
|
||||
template <typename 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
|
||||
|
@ -1637,3 +1637,15 @@ TEST(LiteralsTest, NamedArg) {
|
||||
udl_a_w);
|
||||
}
|
||||
#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));
|
||||
}
|
||||
|
@ -870,15 +870,21 @@ TEST(UtilTest, ReportWindowsError) {
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
TEST(UtilTest, IsConvertibleToInt) {
|
||||
EXPECT_TRUE(fmt::internal::IsConvertibleToInt<char>::value);
|
||||
EXPECT_FALSE(fmt::internal::IsConvertibleToInt<const char *>::value);
|
||||
enum TestEnum2 {};
|
||||
enum TestEnum3 {};
|
||||
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
|
||||
enum TestEnum : char {TestValue};
|
||||
TEST(UtilTest, IsEnumConvertibleToInt) {
|
||||
EXPECT_TRUE(fmt::internal::IsConvertibleToInt<TestEnum>::value);
|
||||
EXPECT_TRUE(fmt::internal::ConvertToInt<TestEnum>::enable_conversion);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user