Skip to content

Commit

Permalink
Fix join for specifc types (fmtlib#1981)
Browse files Browse the repository at this point in the history
Try to apply the same rules for iterator value type (join) as for
regular types.

Try to convert iterator value type to a formattable one.
For instance if the join operation refers to an enum class iterator
(such as std::byte) then try to use the enum class underlying type.

Also try to use a fallback formatter if available.
  • Loading branch information
kamibo committed Nov 25, 2020
1 parent 5f41bb0 commit b47a944
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 4 deletions.
26 changes: 22 additions & 4 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -3674,21 +3674,39 @@ struct arg_join : detail::view {
: begin(b), end(e), sep(s) {}
};

template <typename Char>
using iterator_arg_mapper = detail::arg_mapper<FMT_BUFFER_CONTEXT(Char)>;

template <typename It, typename Char>
using iterator_format_type =
decltype(std::declval<iterator_arg_mapper<Char>>().map(
std::declval<typename std::iterator_traits<It>::value_type>()));

template <typename It, typename Char>
using iterator_formatter = conditional_t<
has_formatter<iterator_format_type<It, Char>,
FMT_BUFFER_CONTEXT(Char)>::value,
formatter<iterator_format_type<It, Char>, Char>,
detail::fallback_formatter<iterator_format_type<It, Char>, Char>>;

template <typename It, typename Sentinel, typename Char>
struct formatter<arg_join<It, Sentinel, Char>, Char>
: formatter<typename std::iterator_traits<It>::value_type, Char> {
: iterator_formatter<It, Char> {
template <typename FormatContext>
auto format(const arg_join<It, Sentinel, Char>& value, FormatContext& ctx)
-> decltype(ctx.out()) {
using base = formatter<typename std::iterator_traits<It>::value_type, Char>;
using base = iterator_formatter<It, Char>;

iterator_arg_mapper<Char> mapper;

auto it = value.begin;
auto out = ctx.out();
if (it != value.end) {
out = base::format(*it++, ctx);
out = base::format(mapper.map(*it++), ctx);
while (it != value.end) {
out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
out = base::format(*it++, ctx);
out = base::format(mapper.map(*it++), ctx);
}
}
return out;
Expand Down
15 changes: 15 additions & 0 deletions test/format-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <cfloat>
#include <climits>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <list>
#include <memory>
Expand Down Expand Up @@ -1761,6 +1762,20 @@ TEST(FormatTest, JoinArg) {
#endif
}

TEST(FormatTest, JoinByte) {
using fmt::join;
#if __cplusplus >= 201703L
using byte = std::byte;
#else
enum class byte : unsigned char {};
#endif

std::vector<std::byte> v = {std::byte{0x1}, std::byte{0x2}};

EXPECT_EQ("(1, 2)", format("({})", join(v, ", ")));
EXPECT_EQ("(0x1, 0x2)", format("({0:#x})", join(v, ", ")));
}

template <typename T> std::string str(const T& value) {
return fmt::format("{}", value);
}
Expand Down
5 changes: 5 additions & 0 deletions test/ostream-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ TEST(OStreamTest, Join) {
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, v + 3, ", ")));
}

TEST(OStreamTest, JoinCustom) {
auto strs = std::vector<TestString>{TestString("foo"), TestString("bar")};
EXPECT_EQ("foo, bar", fmt::format("{}", fmt::join(strs, ", ")));
}

#if FMT_USE_CONSTEXPR
TEST(OStreamTest, ConstexprString) {
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
Expand Down

0 comments on commit b47a944

Please sign in to comment.