mirror of
https://github.com/fmtlib/fmt.git
synced 2025-03-11 22:14:24 +00:00
Move misplaced join overloads to fmt/ranges.h
This commit is contained in:
parent
0b39d67103
commit
50565f9853
@ -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)
|
||||
|
@ -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*.
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "fmt/chrono.h"
|
||||
#include "fmt/ranges.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest-extra.h"
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user