Skip to content

Commit

Permalink
Move misplaced join overloads to fmt/ranges.h
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Jan 2, 2024
1 parent 0b39d67 commit 50565f9
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 124 deletions.
4 changes: 2 additions & 2 deletions include/fmt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
78 changes: 0 additions & 78 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -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*.
Expand Down
84 changes: 77 additions & 7 deletions include/fmt/ranges.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions include/fmt/xchar.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <cwchar>

#include "format.h"
#include "ranges.h"

#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale>
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions test/compile-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <type_traits>

#include "fmt/chrono.h"
#include "fmt/ranges.h"
#include "gmock/gmock.h"
#include "gtest-extra.h"

Expand Down
34 changes: 0 additions & 34 deletions test/format-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 1 addition & 2 deletions test/os-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
36 changes: 35 additions & 1 deletion test/ranges-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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.
Expand Down

0 comments on commit 50565f9

Please sign in to comment.