Skip to content

Commit

Permalink
Use overloaded operator<< for enums if available (#232)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Nov 24, 2015
1 parent 1a2a333 commit 1294f5a
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 3 deletions.
38 changes: 35 additions & 3 deletions format.h
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,7 @@ 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>
Expand All @@ -960,6 +961,37 @@ FMT_CONVERTIBLE_TO_INT(float);
FMT_CONVERTIBLE_TO_INT(double);
FMT_CONVERTIBLE_TO_INT(long double);

struct DummyStream : std::ostream {
// Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);

static DummyStream &get();
};

No &operator<<(std::ostream &, int);
No &operator<<(std::ostream &, unsigned);

template<typename T, bool IS_CONVERTIBLE_TO_INT>
class ConvertToIntImpl {
public:
enum { value = false };
};

template<typename T>
class ConvertToIntImpl<T, true> {
private:
static const T &get();

public:
// Convert to int only if T doesn't have an overloaded operator<<.
enum { value = sizeof(convert(DummyStream::get() << get())) == sizeof(No) };
};

template<typename T>
struct ConvertToInt {
enum { value = ConvertToIntImpl<T, IsConvertibleToInt<T>::value>::value };
};

template<bool B, class T = void>
struct EnableIf {};

Expand Down Expand Up @@ -1113,20 +1145,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
Expand Down
12 changes: 12 additions & 0 deletions test/format-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
9 changes: 9 additions & 0 deletions test/util-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -909,3 +909,12 @@ TEST(UtilTest, Conditional) {
fmt::internal::Conditional<false, int, char>::type *pc = &c;
(void)pc;
}

enum TestEnum2 {};
enum TestEnum3 {};
std::ostream &operator<<(std::ostream &, TestEnum3);

TEST(UtilTest, ConvertToInt) {
EXPECT_TRUE(fmt::internal::ConvertToInt<TestEnum2>::value);
EXPECT_FALSE(fmt::internal::ConvertToInt<TestEnum3>::value);
}

0 comments on commit 1294f5a

Please sign in to comment.