Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -1598,7 +1598,7 @@ class get_locale {
ignore_unused(loc);
::new (&locale_) std::locale(
#if FMT_USE_LOCALE
loc.template get<std::locale>()
loc.template get<std::locale>()
#endif
);
}
Expand Down
33 changes: 20 additions & 13 deletions include/fmt/ranges.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ using make_index_sequence = make_integer_sequence<size_t, N>;
#endif

template <typename T>
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
using tuple_index_sequence =
make_index_sequence<std::tuple_size<remove_cvref_t<T>>::value>;

template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ {
Expand Down Expand Up @@ -282,12 +283,14 @@ template <typename FormatContext> struct format_tuple_element {
FMT_EXPORT
template <typename T> struct is_tuple_like {
static constexpr bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
detail::is_tuple_like_<remove_cvref_t<T>>::value &&
!detail::is_range_<T>::value;
};

FMT_EXPORT
template <typename T, typename C> struct is_tuple_formattable {
static constexpr bool value = detail::is_tuple_formattable_<T, C>::value;
static constexpr bool value =
detail::is_tuple_formattable_<remove_cvref_t<T>, C>::value;
};

template <typename Tuple, typename Char>
Expand Down Expand Up @@ -673,11 +676,11 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {

FMT_EXPORT
template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
const Tuple& tuple;
Tuple tuple;
basic_string_view<Char> sep;

tuple_join_view(const Tuple& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
tuple_join_view(Tuple&& t, basic_string_view<Char> s)
: tuple(std::forward<Tuple>(t)), sep{s} {}
};

// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
Expand All @@ -691,13 +694,13 @@ template <typename Tuple, typename Char>
struct formatter<tuple_join_view<Tuple, Char>, Char,
enable_if_t<is_tuple_like<Tuple>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::tuple_size<Tuple>());
return do_parse(ctx, std::tuple_size<remove_cvref_t<Tuple>>());
}

template <typename FormatContext>
auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size<Tuple>());
return do_format(value, ctx, std::tuple_size<remove_cvref_t<Tuple>>());
}

private:
Expand All @@ -716,7 +719,9 @@ struct formatter<tuple_join_view<Tuple, Char>, Char,
-> const Char* {
auto end = ctx.begin();
#if FMT_TUPLE_JOIN_SPECIFIERS
end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
end =
std::get<std::tuple_size<remove_cvref_t<Tuple>>::value - N>(formatters_)
.parse(ctx);
if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
Expand All @@ -739,8 +744,10 @@ struct formatter<tuple_join_view<Tuple, Char>, Char,
typename FormatContext::iterator {
using std::get;
auto out =
std::get<std::tuple_size<Tuple>::value - N>(formatters_)
.format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
std::get<std::tuple_size<remove_cvref_t<Tuple>>::value - N>(formatters_)
.format(get<std::tuple_size<remove_cvref_t<Tuple>>::value - N>(
value.tuple),
ctx);
if (N <= 1) return out;
out = detail::copy<Char>(value.sep, out);
ctx.advance_to(out);
Expand Down Expand Up @@ -826,9 +833,9 @@ auto join(Range&& r, string_view sep)
* // Output: 1, a
*/
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
FMT_CONSTEXPR auto join(Tuple&& tuple, string_view sep)
-> tuple_join_view<Tuple, char> {
return {tuple, sep};
return {std::forward<Tuple>(tuple), sep};
}

/**
Expand Down
4 changes: 2 additions & 2 deletions include/fmt/xchar.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@ auto join(std::initializer_list<T> list, wstring_view sep)
}

template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
auto join(Tuple&& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<Tuple, wchar_t> {
return {tuple, sep};
return {std::forward<Tuple>(tuple), sep};
}

template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
Expand Down
5 changes: 5 additions & 0 deletions test/ranges-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ TEST(ranges_test, format_tuple) {
EXPECT_TRUE((fmt::is_formattable<std::tuple<int, float>>::value));
}

TEST(ranges_test, format_tuple_rvalue) {
EXPECT_EQ(fmt::format("{}", std::make_tuple(42, 1.5f, "this is tuple", 'i')),
"(42, 1.5, \"this is tuple\", 'i')");
}

struct not_default_formattable {};
struct bad_format {};

Expand Down