Fix join for specifc types (#1981)

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.
This commit is contained in:
Camille Bordignon 2020-11-25 13:51:09 +01:00
parent 5f41bb0f77
commit 10ea9a02f5
3 changed files with 42 additions and 4 deletions

View File

@ -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;

View File

@ -11,6 +11,7 @@
#include <cfloat>
#include <climits>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <list>
#include <memory>
@ -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<byte> v = {static_cast<byte>(0x1), static_cast<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);
}

View File

@ -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")));