Disable range formatter if value type is not formattable (#1885)

This commit is contained in:
Victor Zverovich 2020-09-20 07:53:13 -07:00
parent c46a8de4e1
commit 2f7e08856b
2 changed files with 25 additions and 6 deletions

View File

@ -157,6 +157,9 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
} }
template <typename Range>
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string< template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
typename std::decay<Arg>::type>::value)> typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
@ -182,7 +185,6 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'"; return add_space ? L" '{}'" : L"'{}'";
} }
} // namespace detail } // namespace detail
template <typename T> struct is_tuple_like { template <typename T> struct is_tuple_like {
@ -246,9 +248,15 @@ template <typename T, typename Char> struct is_range {
!std::is_constructible<detail::std_string_view<Char>, T>::value; !std::is_constructible<detail::std_string_view<Char>, T>::value;
}; };
template <typename RangeT, typename Char> template <typename T, typename Char>
struct formatter<RangeT, Char, struct formatter<
enable_if_t<fmt::is_range<RangeT, Char>::value>> { T, Char,
enable_if_t<fmt::is_range<T, Char>::value
// Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
&& has_formatter<detail::value_type<T>, format_context>::value
#endif
>> {
formatting_range<Char> formatting; formatting_range<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
@ -257,8 +265,7 @@ struct formatter<RangeT, Char,
} }
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values, typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
FormatContext& ctx) {
auto out = detail::copy(formatting.prefix, ctx.out()); auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0; size_t i = 0;
auto it = values.begin(); auto it = values.begin();

View File

@ -141,13 +141,16 @@ TEST(RangesTest, FormatStringLike) {
#endif // FMT_USE_STRING_VIEW #endif // FMT_USE_STRING_VIEW
struct zstring_sentinel {}; struct zstring_sentinel {};
bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; } bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
struct zstring { struct zstring {
const char* p; const char* p;
const char* begin() const { return p; } const char* begin() const { return p; }
zstring_sentinel end() const { return {}; } zstring_sentinel end() const { return {}; }
}; };
TEST(RangesTest, JoinSentinel) { TEST(RangesTest, JoinSentinel) {
zstring hello{"hello"}; zstring hello{"hello"};
EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello)); EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello));
@ -189,3 +192,12 @@ TEST(RangesTest, JoinRange) {
const std::vector<int> z(3u, 0); const std::vector<int> z(3u, 0);
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(z, ","))); EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(z, ",")));
} }
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
struct unformattable {};
TEST(RangesTest, UnformattableRange) {
EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>,
fmt::format_context>::value));
}
#endif