Handle implicit conversions in write

This commit is contained in:
Victor Zverovich 2021-10-31 08:09:32 -07:00
parent 5b0aa638cf
commit 028f227752
2 changed files with 36 additions and 19 deletions

View File

@ -42,7 +42,7 @@
#include <utility> // std::swap #include <utility> // std::swap
#ifdef __cpp_lib_bit_cast #ifdef __cpp_lib_bit_cast
#include <bit> // std::bitcast # include <bit> // std::bitcast
#endif #endif
#include "core.h" #include "core.h"
@ -1720,7 +1720,7 @@ inline auto write_significand(Char* out, UInt significand, int significand_size,
out += significand_size + 1; out += significand_size + 1;
Char* end = out; Char* end = out;
int floating_size = significand_size - integral_size; int floating_size = significand_size - integral_size;
for(int i = floating_size / 2; i > 0; --i) { for (int i = floating_size / 2; i > 0; --i) {
out -= 2; out -= 2;
copy2(out, digits2(significand % 100)); copy2(out, digits2(significand % 100));
significand /= 100; significand /= 100;
@ -2082,7 +2082,7 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
return base_iterator(out, it); return base_iterator(out, it);
} }
// FMT_ENABLE_IF() condition separated to workaround MSVC bug // FMT_ENABLE_IF() condition separated to workaround an MSVC bug.
template < template <
typename Char, typename OutputIt, typename T, typename Char, typename OutputIt, typename T,
bool check = bool check =
@ -2133,18 +2133,28 @@ auto write(OutputIt out, const T* value,
return write_ptr<Char>(out, to_uintptr(value), &specs); return write_ptr<Char>(out, to_uintptr(value), &specs);
} }
template <typename Char, typename OutputIt, typename T> // A write overload that handles implicit conversions.
FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> template <typename Char, typename OutputIt, typename T,
typename std::enable_if< typename Context = basic_format_context<OutputIt, Char>>
mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value == FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t<
type::custom_type, std::is_class<T>::value && !is_string<T>::value &&
OutputIt>::type { !std::is_same<T, Char>::value &&
using context_type = basic_format_context<OutputIt, Char>; !std::is_same<const T&,
decltype(arg_mapper<Context>().map(value))>::value,
OutputIt> {
return write<Char>(out, arg_mapper<Context>().map(value));
}
template <typename Char, typename OutputIt, typename T,
typename Context = basic_format_context<OutputIt, Char>>
FMT_CONSTEXPR auto write(OutputIt out, const T& value)
-> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type,
OutputIt> {
using formatter_type = using formatter_type =
conditional_t<has_formatter<T, context_type>::value, conditional_t<has_formatter<T, Context>::value,
typename context_type::template formatter_type<T>, typename Context::template formatter_type<T>,
fallback_formatter<T, Char>>; fallback_formatter<T, Char>>;
context_type ctx(out, {}, {}); auto ctx = Context(out, {}, {});
return formatter_type().format(value, ctx); return formatter_type().format(value, ctx);
} }

View File

@ -1820,13 +1820,20 @@ struct formatter<adl_test::fmt::detail::foo> : formatter<std::string> {
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
TEST(format_test, to_string) { struct convertible_to_int {
EXPECT_EQ("42", fmt::to_string(42)); operator int() const { return value; }
EXPECT_EQ("0x1234", fmt::to_string(reinterpret_cast<void*>(0x1234)));
EXPECT_EQ("foo", fmt::to_string(adl_test::fmt::detail::foo()));
enum test_enum2 : unsigned char { test_value }; int value = 42;
EXPECT_EQ("0", fmt::to_string(test_value)); };
TEST(format_test, to_string) {
EXPECT_EQ(fmt::to_string(42), "42");
EXPECT_EQ(fmt::to_string(reinterpret_cast<void*>(0x1234)), "0x1234");
EXPECT_EQ(fmt::to_string(adl_test::fmt::detail::foo()), "foo");
EXPECT_EQ(fmt::to_string(convertible_to_int()), "42");
enum foo : unsigned char { zero };
EXPECT_EQ(fmt::to_string(zero), "0");
} }
TEST(format_test, output_iterators) { TEST(format_test, output_iterators) {