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:
parent
5f41bb0f77
commit
10ea9a02f5
@ -3674,21 +3674,39 @@ struct arg_join : detail::view {
|
|||||||
: begin(b), end(e), sep(s) {}
|
: 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>
|
template <typename It, typename Sentinel, typename Char>
|
||||||
struct formatter<arg_join<It, Sentinel, Char>, 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>
|
template <typename FormatContext>
|
||||||
auto format(const arg_join<It, Sentinel, Char>& value, FormatContext& ctx)
|
auto format(const arg_join<It, Sentinel, Char>& value, FormatContext& ctx)
|
||||||
-> decltype(ctx.out()) {
|
-> 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 it = value.begin;
|
||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
if (it != value.end) {
|
if (it != value.end) {
|
||||||
out = base::format(*it++, ctx);
|
out = base::format(mapper.map(*it++), ctx);
|
||||||
while (it != value.end) {
|
while (it != value.end) {
|
||||||
out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
|
out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
|
||||||
ctx.advance_to(out);
|
ctx.advance_to(out);
|
||||||
out = base::format(*it++, ctx);
|
out = base::format(mapper.map(*it++), ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -1761,6 +1762,20 @@ TEST(FormatTest, JoinArg) {
|
|||||||
#endif
|
#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) {
|
template <typename T> std::string str(const T& value) {
|
||||||
return fmt::format("{}", value);
|
return fmt::format("{}", value);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -188,6 +188,11 @@ TEST(OStreamTest, Join) {
|
|||||||
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, v + 3, ", ")));
|
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
|
#if FMT_USE_CONSTEXPR
|
||||||
TEST(OStreamTest, ConstexprString) {
|
TEST(OStreamTest, ConstexprString) {
|
||||||
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
|
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user