Move misplaced join overloads to fmt/ranges.h

This commit is contained in:
Victor Zverovich 2024-01-01 16:21:21 -08:00
parent 0b39d67103
commit 50565f9853
8 changed files with 123 additions and 124 deletions

View File

@ -321,8 +321,8 @@ template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
constexpr FMT_INLINE auto is_constant_evaluated(
bool default_value = false) noexcept -> bool {
// Workaround for incompatibility between libstdc++ consteval-based
// std::is_constant_evaluated() implementation and clang-14.
// https://github.com/fmtlib/fmt/issues/3247
// std::is_constant_evaluated() implementation and clang-14:
// https://github.com/fmtlib/fmt/issues/3247.
#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \
_GLIBCXX_RELEASE >= 12 && \
(FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500)

View File

@ -4224,84 +4224,6 @@ template <typename T> struct nested_formatter {
}
};
// DEPRECATED! join_view will be moved to ranges.h.
template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
It begin;
Sentinel end;
basic_string_view<Char> sep;
join_view(It b, Sentinel e, basic_string_view<Char> s)
: begin(b), end(e), sep(s) {}
};
template <typename It, typename Sentinel, typename Char>
struct formatter<join_view<It, Sentinel, Char>, Char> {
private:
using value_type =
#ifdef __cpp_lib_ranges
std::iter_value_t<It>;
#else
typename std::iterator_traits<It>::value_type;
#endif
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
public:
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
return value_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(const join_view<It, Sentinel, Char>& value,
FormatContext& ctx) const -> decltype(ctx.out()) {
auto it = value.begin;
auto out = ctx.out();
if (it != value.end) {
out = value_formatter_.format(*it, ctx);
++it;
while (it != value.end) {
out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
out = value_formatter_.format(*it, ctx);
++it;
}
}
return out;
}
};
/**
Returns a view that formats the iterator range `[begin, end)` with elements
separated by `sep`.
*/
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
return {begin, end, sep};
}
/**
\rst
Returns a view that formats `range` with elements separated by `sep`.
**Example**::
std::vector<int> v = {1, 2, 3};
fmt::print("{}", fmt::join(v, ", "));
// Output: "1, 2, 3"
``fmt::join`` applies passed format specifiers to the range elements::
fmt::print("{:02}", fmt::join(v, ", "));
// Output: "01, 02, 03"
\endrst
*/
template <typename Range>
auto join(Range&& range, string_view sep)
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> {
return join(std::begin(range), std::end(range), sep);
}
/**
\rst
Converts *value* to ``std::string`` using the default format for type *T*.

View File

@ -574,6 +574,83 @@ struct formatter<
Char> {
};
template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
It begin;
Sentinel end;
basic_string_view<Char> sep;
join_view(It b, Sentinel e, basic_string_view<Char> s)
: begin(b), end(e), sep(s) {}
};
template <typename It, typename Sentinel, typename Char>
struct formatter<join_view<It, Sentinel, Char>, Char> {
private:
using value_type =
#ifdef __cpp_lib_ranges
std::iter_value_t<It>;
#else
typename std::iterator_traits<It>::value_type;
#endif
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
public:
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
return value_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(const join_view<It, Sentinel, Char>& value,
FormatContext& ctx) const -> decltype(ctx.out()) {
auto it = value.begin;
auto out = ctx.out();
if (it != value.end) {
out = value_formatter_.format(*it, ctx);
++it;
while (it != value.end) {
out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
out = value_formatter_.format(*it, ctx);
++it;
}
}
return out;
}
};
/**
Returns a view that formats the iterator range `[begin, end)` with elements
separated by `sep`.
*/
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
return {begin, end, sep};
}
/**
\rst
Returns a view that formats `range` with elements separated by `sep`.
**Example**::
std::vector<int> v = {1, 2, 3};
fmt::print("{}", fmt::join(v, ", "));
// Output: "1, 2, 3"
``fmt::join`` applies passed format specifiers to the range elements::
fmt::print("{:02}", fmt::join(v, ", "));
// Output: "01, 02, 03"
\endrst
*/
template <typename Range>
auto join(Range&& range, string_view sep)
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> {
return join(std::begin(range), std::end(range), sep);
}
template <typename Char, typename... T> struct tuple_join_view : detail::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
@ -708,13 +785,6 @@ FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep};
}
/**
\rst
Returns an object that formats `initializer_list` with elements separated by

View File

@ -11,6 +11,7 @@
#include <cwchar>
#include "format.h"
#include "ranges.h"
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale>
@ -96,6 +97,12 @@ auto join(std::initializer_list<T> list, wstring_view sep)
return join(std::begin(list), std::end(list), sep);
}
template <typename... T>
auto join(const std::tuple<T...>& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)

View File

@ -10,6 +10,7 @@
#include <type_traits>
#include "fmt/chrono.h"
#include "fmt/ranges.h"
#include "gmock/gmock.h"
#include "gtest-extra.h"

View File

@ -1791,40 +1791,6 @@ TEST(format_test, nested_formatter) {
enum test_enum { foo, bar };
auto format_as(test_enum e) -> int { return e; }
TEST(format_test, join) {
using fmt::join;
int v1[3] = {1, 2, 3};
auto v2 = std::vector<float>();
v2.push_back(1.2f);
v2.push_back(3.4f);
void* v3[2] = {&v1[0], &v1[1]};
EXPECT_EQ(fmt::format("({})", join(v1, v1 + 3, ", ")), "(1, 2, 3)");
EXPECT_EQ(fmt::format("({})", join(v1, v1 + 1, ", ")), "(1)");
EXPECT_EQ(fmt::format("({})", join(v1, v1, ", ")), "()");
EXPECT_EQ(fmt::format("({:03})", join(v1, v1 + 3, ", ")), "(001, 002, 003)");
EXPECT_EQ("(+01.20, +03.40)",
fmt::format("({:+06.2f})", join(v2.begin(), v2.end(), ", ")));
EXPECT_EQ(fmt::format("{0:{1}}", join(v1, v1 + 3, ", "), 1), "1, 2, 3");
EXPECT_EQ(fmt::format("{}, {}", v3[0], v3[1]),
fmt::format("{}", join(v3, v3 + 2, ", ")));
EXPECT_EQ(fmt::format("({})", join(v1, ", ")), "(1, 2, 3)");
EXPECT_EQ(fmt::format("({:+06.2f})", join(v2, ", ")), "(+01.20, +03.40)");
auto v4 = std::vector<test_enum>{foo, bar, foo};
EXPECT_EQ(fmt::format("{}", join(v4, " ")), "0 1 0");
}
#ifdef __cpp_lib_byte
TEST(format_test, join_bytes) {
auto v = std::vector<std::byte>{std::byte(1), std::byte(2), std::byte(3)};
EXPECT_EQ(fmt::format("{}", fmt::join(v, ", ")), "1, 2, 3");
}
#endif
std::string vformat_message(int id, const char* format, fmt::format_args args) {
auto buffer = fmt::memory_buffer();
fmt::format_to(fmt::appender(buffer), "[{}] ", id);

View File

@ -252,8 +252,7 @@ TEST(ostream_test, move_while_holding_data) {
TEST(ostream_test, print) {
fmt::ostream out = fmt::output_file("test-file");
out.print("The answer is {}.\n",
fmt::join(std::initializer_list<int>{42}, ", "));
out.print("The answer is {}.\n", 42);
out.close();
file in("test-file", file::RDONLY);
EXPECT_READ(in, "The answer is 42.\n");

View File

@ -287,7 +287,7 @@ TEST(ranges_test, range) {
EXPECT_EQ(fmt::format("{}", z), "[0, 0, 0]");
}
enum test_enum { foo };
enum test_enum { foo, bar };
auto format_as(test_enum e) -> int { return e; }
TEST(ranges_test, enum_range) {
@ -302,6 +302,40 @@ TEST(ranges_test, unformattable_range) {
}
#endif
TEST(ranges_test, join) {
using fmt::join;
int v1[3] = {1, 2, 3};
auto v2 = std::vector<float>();
v2.push_back(1.2f);
v2.push_back(3.4f);
void* v3[2] = {&v1[0], &v1[1]};
EXPECT_EQ(fmt::format("({})", join(v1, v1 + 3, ", ")), "(1, 2, 3)");
EXPECT_EQ(fmt::format("({})", join(v1, v1 + 1, ", ")), "(1)");
EXPECT_EQ(fmt::format("({})", join(v1, v1, ", ")), "()");
EXPECT_EQ(fmt::format("({:03})", join(v1, v1 + 3, ", ")), "(001, 002, 003)");
EXPECT_EQ("(+01.20, +03.40)",
fmt::format("({:+06.2f})", join(v2.begin(), v2.end(), ", ")));
EXPECT_EQ(fmt::format("{0:{1}}", join(v1, v1 + 3, ", "), 1), "1, 2, 3");
EXPECT_EQ(fmt::format("{}, {}", v3[0], v3[1]),
fmt::format("{}", join(v3, v3 + 2, ", ")));
EXPECT_EQ(fmt::format("({})", join(v1, ", ")), "(1, 2, 3)");
EXPECT_EQ(fmt::format("({:+06.2f})", join(v2, ", ")), "(+01.20, +03.40)");
auto v4 = std::vector<test_enum>{foo, bar, foo};
EXPECT_EQ(fmt::format("{}", join(v4, " ")), "0 1 0");
}
#ifdef __cpp_lib_byte
TEST(ranges_test, join_bytes) {
auto v = std::vector<std::byte>{std::byte(1), std::byte(2), std::byte(3)};
EXPECT_EQ(fmt::format("{}", fmt::join(v, ", ")), "1, 2, 3");
}
#endif
#ifdef FMT_RANGES_TEST_ENABLE_JOIN
TEST(ranges_test, join_tuple) {
// Value tuple args.