Skip to content

Commit

Permalink
Fix handling of implicit conversion to integral types larger than int
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Feb 2, 2018
1 parent 08dff37 commit 1c7b751
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 25 deletions.
61 changes: 36 additions & 25 deletions include/fmt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,22 +353,6 @@ inline void require_wchar() {
"formatting of wide characters into a narrow output is disallowed");
}

template <typename T, bool ENABLE = true>
struct convert_to_int {
enum {
value = !std::is_arithmetic<T>::value && std::is_convertible<T, int>::value
};
};

#define FMT_DISABLE_CONVERSION_TO_INT(Type) \
template <> \
struct convert_to_int<Type> { enum { value = 0 }; }

// Silence warnings about convering float to int.
FMT_DISABLE_CONVERSION_TO_INT(float);
FMT_DISABLE_CONVERSION_TO_INT(double);
FMT_DISABLE_CONVERSION_TO_INT(long double);

template <typename Char>
struct named_arg_base;

Expand Down Expand Up @@ -400,12 +384,46 @@ constexpr bool is_arithmetic(type t) {
return t > internal::NONE && t <= internal::LAST_NUMERIC_TYPE;
}

template <typename T, bool ENABLE = true>
struct convert_to_int {
enum {
value = !std::is_arithmetic<T>::value && std::is_convertible<T, int>::value
};
};

#define FMT_DISABLE_CONVERSION_TO_INT(Type) \
template <> \
struct convert_to_int<Type> { enum { value = 0 }; }

// Silence warnings about convering float to int.
FMT_DISABLE_CONVERSION_TO_INT(float);
FMT_DISABLE_CONVERSION_TO_INT(double);
FMT_DISABLE_CONVERSION_TO_INT(long double);

// Disambiguates conversions to different integral types.
struct type_selector {
using char2 = struct { char a[2]; };
static char convert(...);
static char convert(int);
static char convert(unsigned);
static char convert(long);
static char convert(unsigned long);
static char2 convert(long long);
static char2 convert(unsigned long long);

template <typename T>
static constexpr type select() {
return sizeof(convert(std::declval<T>())) == 1 ? INT : LONG_LONG;
}
};

template <typename T>
constexpr type get_type() {
return std::is_reference<T>::value || std::is_array<T>::value ?
get_type<typename std::decay<T>::type>() :
(is_named_arg<T>::value ?
NAMED_ARG : (convert_to_int<T>::value ? INT : CUSTOM));
NAMED_ARG : (convert_to_int<T>::value ?
type_selector::select<T>() : CUSTOM));
}

template <> constexpr type get_type<bool>() { return BOOL; }
Expand Down Expand Up @@ -549,13 +567,6 @@ class value {

value(std::nullptr_t) { pointer = nullptr; }

template <typename T>
value(const T &val,
typename std::enable_if<convert_to_int<T>::value, int>::type = 0) {
static_assert(get_type<T>() == INT, "invalid type");
int_value = val;
}

template <typename T>
value(const T &val,
typename std::enable_if<!convert_to_int<T>::value, int>::type = 0) {
Expand Down Expand Up @@ -737,7 +748,7 @@ constexpr basic_arg<Context> make_arg(const T &value) {
template <bool IS_PACKED, typename Context, typename T>
inline typename std::enable_if<IS_PACKED, value<Context>>::type
make_arg(const T &value) {
return value;
return {value};
}

template <bool IS_PACKED, typename Context, typename T>
Expand Down
8 changes: 8 additions & 0 deletions test/format-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,14 @@ TEST(FormatterTest, FormatIntLocale) {
EXPECT_EQ("1,234,567", format("{:n}", 1234567));
}

struct ConvertibleToLongLong {
operator long long() const { return 1LL << 32; }
};

TEST(FormatterTest, FormatConvertibleToLongLong) {
EXPECT_EQ("100000000", format("{:x}", ConvertibleToLongLong()));
}

TEST(FormatterTest, FormatFloat) {
EXPECT_EQ("392.500000", format("{0:f}", 392.5f));
}
Expand Down

0 comments on commit 1c7b751

Please sign in to comment.